# 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: ```js $: 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: ```js // 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. ```js $: 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) ```js // 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` ```js // 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) ```js // 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 ```js // 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 ```js // 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) ```js 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 ```js note("c4 e4 g4 c5") .sound("triangle") .lpf(3000) .decay(0.15).sustain(0).release(0.3) ``` ### Pad Sound ```js 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) ```js 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 ```js // 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 ```js // 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 ```js // 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 ```js // 3 against 4 note("c3 e3 g3, c4 e4 g4 b4").sound("piano") ``` ### Euclidean Rhythms ```js // Euclidean distribution of 3 hits in 8 steps sound("{bd bd bd}%8") ``` ### Probability ```js // Random elements s("bd sd? hh bd").bank("RolandTR909") // sd has 50% chance ``` ### Progressive Transformation ```js // 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: ```js // 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 ```js note("<[c4 e4 g4 ~] [~ ~ ~ [b3 c4]]>") .sound("piano") ``` ### Rhythmic Chord Stabs ```js 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.