Files
Strudel-AI-music/agentic_workflow/prod_tips.md
2026-03-20 21:16:20 +01:00

5.4 KiB

Production Tips for Strudel

Professional-level techniques for polished output.

1. Mixing in Strudel

Gain Staging

Always set explicit gain values. Elements should not fight for space:

$: s("bd*4").gain(1.1)              // kick = loudest
$: s("[~ sd]*2").gain(0.7)          // snare = clear
$: s("[~ hh]*4").gain(0.25)         // hats = subtle
$: note("c2*4").s("sine").gain(0.5) // bass = strong
$: note("[c4,e4,g4]").s("triangle").gain(0.15) // pad = background
$: note("c5 e5 g5").s("sine").gain(0.1)        // melody = present but soft

Frequency Separation

Each layer should own a frequency range. Use .lpf() and .hpf() to carve space:

// Sub bass: ONLY low frequencies
note("c1").sound("sine").lpf(80).gain(0.6)

// Mid bass: remove sub, keep warmth
note("c2").sound("sawtooth").lpf(600).hpf(80).gain(0.3)

// Pad: remove low-end mud
note("[c3,e3,g3]").sound("triangle").hpf(300).lpf(3000).gain(0.15)

// Hi-hats: only highs
s("hh*8").hpf(3000).gain(0.25)

Stereo Width with Pan

Center: kick, bass, snare. Sides: hats, pads, FX.

$: s("bd*4").gain(1)                              // center (default)
$: s("hh*8").gain(0.2).pan(sine.range(0.3, 0.7))  // gentle side movement
$: note("[c4,e4,g4]").s("saw").gain(0.1).jux(rev)  // wide stereo

2. Movement & Variation

Filter Automation (Essential for Non-Static Sound)

// Slow sweep over 8 cycles
.lpf(sine.range(300, 3000).slow(8))

// Fast wobble
.lpf(sine.range(200, 1500).fast(2))

// Random variation
.lpf(perlin.range(500, 2000))

// Stepped changes
.lpf("<400 800 1200 2000>")

Rhythmic Variation with every

// Reverse every 4th cycle
s("bd sd [~ bd] sd").every(4, x => x.rev())

// Double speed every 3rd cycle (fill)
s("bd sd [~ bd] sd").every(3, x => x.ply(2))

// Add filter sweep every 8th cycle
s("hh*16").every(8, x => x.lpf(sine.range(500, 5000)))

The off Technique (Ghost Notes / Echoes)

// Ghost notes: offset copy played softer and pitch-shifted
n("0 2 4 6").scale("C:minor")
  .off(1/8, x => x.add(7).gain(0.3))
  .sound("piano")

Humanize with Randomness

// Random gain variation (human feel)
s("hh*16").gain(perlin.range(0.1, 0.35))

// Random panning
s("rim*4").pan(rand)

// Sometimes skip notes
s("hh*16").sometimesBy(0.2, x => x.gain(0))

3. Sound Design Techniques

Layering Synths

// Fat bass: two detuned saws
note("c2").sound("sawtooth,sawtooth").detune("0 0.3")

// Rich pad: multiple waveforms
note("[c3,e3,g3]").sound("sawtooth,triangle,square")
  .gain("0.15 0.1 0.05")

Acid Bass (Resonant Filter)

note("c2 c2 [c2 eb2] c2")
  .sound("sawtooth")
  .lpf(sine.range(200, 4000).fast(2))
  .lpq(10)  // high resonance = acid sound
  .decay(0.1).sustain(0)

Pluck Sound

note("c4 e4 g4 c5")
  .sound("triangle")
  .lpf(3000)
  .decay(0.15).sustain(0).release(0.3)

Pad Sound

note("[c3,e3,g3,b3]")
  .sound("sawtooth")
  .lpf(800).lpq(1)
  .attack(0.5).release(1.5)
  .room(0.7).size(0.9)

Sub Bass (808 Style)

note("c1").sound("sine")
  .lpf(100).gain(0.6)
  .decay(0.8).sustain(0)

4. Arrangement in a Single Loop

Since Strudel works in cycles, use <> and /N for section variation:

A/B Pattern Alternation

// Drums: minimal A, full B
$: s("<[bd ~ ~ ~]*4 [bd*4]>/8")  // kick: sparse then full every 8 cycles
$: s("<[~ ~ ~ ~]*4 [[~ sd]*2]*4>/8")  // snare: silent then enters

Build-up Effect

// Filter opens gradually over 16 cycles
$: note("c2*4").sound("sawtooth")
  .lpf(sine.range(200, 4000).slow(16))

// Hats get denser
$: s("hh*<4 8 12 16>")
  .gain(0.2)

Drop

// Build: filtered, no kick, rising
// Drop: everything at once, filter open
$: s("<[~ ~ ~ ~]*4 [bd*4]*4>/8")  // kick enters at drop
$: note("c2*4").sound("sawtooth")
  .lpf(saw.range(200, 3000).slow(8))  // filter opens

5. Advanced Pattern Techniques

Polyrhythm

// 3 against 4
note("c3 e3 g3, c4 e4 g4 b4").sound("piano")

Euclidean Rhythms

// Euclidean distribution of 3 hits in 8 steps
sound("{bd bd bd}%8")

Probability

// Random elements
s("bd sd? hh bd").bank("RolandTR909")  // sd has 50% chance

Progressive Transformation

// Pattern morphs over time using every + sometimes
n("0 2 4 6").scale("C:minor").sound("piano")
  .every(4, x => x.add(2))
  .every(3, x => x.rev())
  .sometimes(x => x.delay(0.5))

6. Common Professional Patterns

Side-chain Simulation

Strudel can't do real sidechain, but you can simulate pump:

// Pad volume ducks on kick hits
note("[c3,e3,g3]").sound("sawtooth")
  .gain("0.05 0.15 0.2 0.15")  // quiet on beat 1 (where kick is)
  .lpf(1200).room(0.5)

Call and Response

note("<[c4 e4 g4 ~] [~ ~ ~ [b3 c4]]>")
  .sound("piano")

Rhythmic Chord Stabs

note("<[c3,eb3,g3] ~ ~ ~ [ab2,c3,eb3] ~ ~ ~>")
  .sound("square")
  .decay(0.05).sustain(0).gain(0.15)

7. Debugging Checklist

If it sounds wrong:

  1. No sound? — Check .sound() is set. Check gain > 0. Check lpf isn't too low.
  2. Too muddy? — Add .hpf() to pads/mids. Reduce room on bass.
  3. Too static? — Add signal modulation to filters. Use every() for variation.
  4. Timing weird? — Check setcps() formula. Verify mini-notation subdivisions.
  5. Clashing notes? — Use .scale() to stay in key. Check octave numbers.
  6. Too loud/quiet? — Review gain staging. Kick ~1.0, everything else lower.