โ† JamDojo Sound Design & Timbre

Sound Design & Timbre

Timbre is the โ€œcolorโ€ or โ€œtextureโ€ of soundโ€”what makes a piano sound different from a guitar playing the same note. This guide teaches you to sculpt timbre using Strudelโ€™s synthesis tools: oscillators, filters, envelopes, and effects.

What Shapes Timbre?

Four main elements determine how a sound feels:

  1. Waveform โ€” The raw shape of vibration (sine, saw, square)
  2. Harmonics โ€” The overtones present above the fundamental note
  3. Envelope โ€” How the sound evolves over time (attack, decay, sustain, release)
  4. Filtering โ€” Which frequencies are emphasized or removed

Letโ€™s explore each one.


Building Blocks: Waveforms

Strudelโ€™s built-in oscillators give you different starting materials:

Sine Wave

Pure tone, no harmonics. Soft and flute-like:

s("sine").note("c4").room(.3)._scope()

Sawtooth Wave

All harmonics present. Bright and buzzyโ€”the foundation of most synth sounds:

s("sawtooth").note("c4").room(.3)._scope()

Square Wave

Only odd harmonics. Hollow and reedy, like a clarinet:

s("square").note("c4").room(.3)._scope()

Triangle Wave

Softer odd harmonics. Somewhere between sine and square:

s("triangle").note("c4").room(.3)._scope()

Compare all four:

s("<sine sawtooth square triangle>")
.note("c4")
.room(.3)._scope()

Quick Reference: Waveforms

WaveformHarmonicsCharacterGood For
sineNone (pure)Soft, cleanSub bass, flutes, pure tones
sawtoothAllBright, buzzyLeads, pads, brass
squareOdd onlyHollow, reedyClarinets, retro games
triangleSoft oddMellowSoft leads, mellow bass

Harmonics & Partials

Timbre comes from harmonicsโ€”additional frequencies above the fundamental note. You can design custom waveforms using partials:

Just the fundamental (like a sine):

s("user").partials([1]).note("c4").room(.3)._scope()

Add the second harmonic (octave above):

s("user").partials([1, 0.5]).note("c4").room(.3)._scope()

More harmonics = brighter sound:

s("user").partials([1, 0.5, 0.3, 0.2, 0.1]).note("c4").room(.3)._scope()

Odd harmonics only (square-like):

s("user").partials([1, 0, 0.3, 0, 0.2, 0, 0.1]).note("c4").room(.3)._scope()

Noise

Noise adds texture, breath, and percussive character:

White Noise

Equal energy across all frequencies. Bright and hissy:

s("white").room(.3)._scope()

Pink Noise

Less high frequencies. Warmer, like wind or rain:

s("pink").room(.3)._scope()

Brown Noise

Even fewer highs. Deep rumble:

s("brown").room(.3)._scope()

Mix noise with an oscillator for texture:

const noiseAmt = slider(0.1, 0, 0.5)
s("sawtooth").note("c4").noise(noiseAmt).room(.3)._scope()

Shaping Sound with Envelopes

An envelope controls how a parameter changes over time. The most common is ADSR for amplitude:

  • Attack โ€” Time to reach full volume
  • Decay โ€” Time to fall to sustain level
  • Sustain โ€” Level held while note plays (0-1)
  • Release โ€” Time to fade after note ends

Attack: Slow vs Fast

Use the slider to hear how attack changes the feel from percussive to pad-like:

const att = slider(0.001, 0.001, 1)
s("sawtooth").note("c4")
.attack(att).decay(.3).sustain(.5).release(.3)
.room(.3)._scope()

Decay & Sustain

Adjust decay and sustain to shape the body of the sound:

const dec = slider(0.15, 0.05, 1)
const sus = slider(0, 0, 1)
s("sawtooth").note("c4")
.attack(.001).decay(dec).sustain(sus).release(.3)
.room(.3)._scope()

Release

Control how long the sound lingers after the note ends:

const rel = slider(0.05, 0.01, 2)
s("sawtooth").note("c4")
.attack(.01).decay(.2).sustain(.5).release(rel)
.room(.3)._scope()

Filter Envelopes

Filters can also have envelopes, making sounds โ€œopen upโ€ or โ€œclose downโ€ over time:

const envAmt = slider(4, 0, 10)
const lpAtt = slider(0.001, 0.001, 0.5)
const lpDec = slider(0.3, 0.05, 1)
s("sawtooth").note("c3")
.lpf(200).lpenv(envAmt)
.lpattack(lpAtt).lpdecay(lpDec).lpsustain(0)
.room(.3)._scope()

Pitch Envelopes

Pitch envelope bends the note up or down at the startโ€”essential for kicks and zaps:

Kick drum pitch drop:

const pitchAmt = slider(24, 0, 48)
const pitchDec = slider(0.05, 0.01, 0.2)
const ampDec = slider(0.3, 0.1, 1)
s("sine").note("c1")
.penv(pitchAmt).pdecay(pitchDec)
.decay(ampDec).sustain(0)
.room(.2)._scope()

Laser zap:

const pitchAmt = slider(-36, -48, 0)
const pitchDec = slider(0.1, 0.02, 0.3)
s("sawtooth").note("c4")
.penv(pitchAmt).pdecay(pitchDec)
.decay(.2).sustain(0)
.lpf(2000)
.room(.3)._scope()

Filters & Frequency Control

Filters remove frequencies, sculpting the tone:

Low-Pass Filter (lpf)

Removes high frequencies. Lower cutoff = darker sound:

const cutoff = slider(2000, 100, 8000)
s("sawtooth").note("c4").lpf(cutoff).room(.3)._scope()

High-Pass Filter (hpf)

Removes low frequencies. Thins out the sound:

const cutoff = slider(500, 100, 4000)
s("sawtooth").note("c4").hpf(cutoff).room(.3)._scope()

Resonance (lpq)

Boosts frequencies around the cutoff. High resonance creates a โ€œwahโ€ peak:

const cutoff = slider(600, 200, 2000)
const res = slider(5, 0, 20)
s("sawtooth").note("c3").lpf(cutoff).lpq(res).room(.3)._scope()

Vowel Filter

Shapes sound like vocal formants:

s("sawtooth").note("c4").vowel("<a e i o u>").room(.3)._scope()

Quick Reference: Filters

FilterFunctionUse Case
lpf(hz)Remove highsWarmth, darkness
hpf(hz)Remove lowsThinning, clarity
lpq(n)Resonance peakWah, acid sounds
vowel(v)Formant shapingVocal textures

Classic Synth Sounds

Now letโ€™s build some iconic sounds step by step.

808 Bass (Detailed Walkthrough)

The Roland TR-808 bass is a sine wave with a pitch envelope dropping from high to low:

Step 1: Start with a sine wave

s("sine").note("c1").decay(.5).sustain(0).room(.2)._scope()

Step 2: Add pitch envelope (the characteristic โ€œboomโ€)

s("sine").note("c1")
.penv(12).pdecay(.08)
.decay(.5).sustain(0)
.room(.2)._scope()

Step 3: Shape your 808

const pitchAmt = slider(12, 0, 36)
const pitchDec = slider(0.08, 0.02, 0.2)
const ampDec = slider(0.5, 0.1, 1.5)
s("sine").note("c1")
.penv(pitchAmt).pdecay(pitchDec)
.decay(ampDec).sustain(0)
.room(.2)._scope()

Acid Squelch (Detailed Walkthrough)

The TB-303 acid sound uses a sawtooth with resonant filter envelope:

Step 1: Sawtooth bass

s("sawtooth").note("c2").decay(.2).sustain(0).room(.2)._scope()

Step 2: Add low-pass filter

s("sawtooth").note("c2").lpf(400).decay(.2).sustain(0).room(.2)._scope()

Step 3: Add resonance (the squelch)

s("sawtooth").note("c2").lpf(400).lpq(12).decay(.2).sustain(0).room(.2)._scope()

Step 4: Add filter envelope (the wah)

s("sawtooth").note("c2")
.lpf(200).lpq(15).lpenv(6)
.lpattack(.001).lpdecay(.15).lpsustain(0)
.decay(.2).sustain(0)
.room(.2)._scope()

Full acid line โ€” shape the squelch:

const res = slider(15, 0, 25)
const envAmt = slider(6, 0, 12)
const envDec = slider(0.1, 0.02, 0.3)
note("c2 c2 c3 c2 eb2 c2 f2 c2")
.s("sawtooth")
.lpf(200).lpq(res).lpenv(envAmt)
.lpattack(.001).lpdecay(envDec).lpsustain(0)
.decay(.15).sustain(0)
.room(.2)._scope()

Supersaw Lead

The supersaw uses multiple detuned oscillators for a thick, moving sound:

s("supersaw").note("c4 e4 g4 c5")
.room(.3)._scope()

Control thickness with unison and spread:

const voices = slider(5, 1, 9)
const detune = slider(0.5, 0, 1)
const cutoff = slider(3000, 500, 8000)
s("supersaw").note("c4")
.unison(voices).spread(detune)
.lpf(cutoff)
.room(.3)._scope()

Pluck

Short decay + filter envelope:

s("sawtooth").note("c4 e4 g4 c5")
.lpf(500).lpenv(4)
.lpattack(.001).lpdecay(.1).lpsustain(0)
.decay(.2).sustain(0)
.room(.3)._scope()

Sub Bass

Pure low-end, minimal harmonics:

s("sine").note("c1")
.decay(.3).sustain(.5).release(.1)
.room(.1)._scope()

Quick Reference: Classic Synth Sounds

SoundBaseKey Settings
808 Basssinepenv(12), pdecay(.08), note(c1)
Acidsawtoothlpf(200), lpq(15), lpenv(6)
Supersawsupersawunison(5), spread(.5)
Plucksawtoothshort decay, lpenv
Sub Basssinelow note, minimal processing

Imitating Acoustic Instruments

Strings (Detailed Walkthrough)

Strings have a slow attack and vibrato:

Step 1: Sawtooth base (rich harmonics like bowed strings)

s("sawtooth").note("c4").room(.3)._scope()

Step 2: Slow attack (bowing in)

s("sawtooth").note("c4")
.attack(.3).decay(.1).sustain(.8).release(.5)
.room(.3)._scope()

Step 3: Add low-pass filter (mellower tone)

s("sawtooth").note("c4")
.attack(.3).decay(.1).sustain(.8).release(.5)
.lpf(2000)
.room(.3)._scope()

Step 4: Add vibrato

s("sawtooth").note("c4")
.attack(.3).decay(.1).sustain(.8).release(.5)
.lpf(2000)
.vib(5).vibmod(.2)
.room(.3)._scope()

String pad chord โ€” shape the warmth:

const att = slider(0.4, 0.1, 1)
const cutoff = slider(1800, 500, 4000)
const vibSpeed = slider(4, 2, 8)
const vibDepth = slider(0.15, 0, 0.4)
chord("<C Am F G>").voicing()
.s("sawtooth")
.attack(att).decay(.1).sustain(.7).release(.6)
.lpf(cutoff).vib(vibSpeed).vibmod(vibDepth)
.room(.4)._scope()

Brass (Detailed Walkthrough)

Brass has a filter envelope that opens with the attack:

Step 1: Sawtooth base

s("sawtooth").note("c4").room(.3)._scope()

Step 2: Add attack time

s("sawtooth").note("c4")
.attack(.08).decay(.1).sustain(.7).release(.2)
.room(.3)._scope()

Step 3: Filter opens with note (the โ€œblatโ€)

s("sawtooth").note("c4")
.attack(.05).decay(.1).sustain(.7).release(.15)
.lpf(400).lpenv(5)
.lpattack(.05).lpdecay(.15).lpsustain(.6)
.room(.3)._scope()

Brass stab:

chord("<C7 F7>").voicing()
.s("sawtooth")
.attack(.03).decay(.15).sustain(.5).release(.1)
.lpf(600).lpenv(4).lpattack(.02).lpdecay(.1).lpsustain(.5)
.room(.3)._scope()

Piano-like

Piano has fast attack, medium decay, and filter movement:

s("sawtooth").note("c4 e4 g4 c5")
.attack(.001).decay(.6).sustain(.2).release(.3)
.lpf(1500).lpenv(3).lpdecay(.4).lpsustain(.3)
.room(.3)._scope()

Flute / Woodwind

Sine or triangle with breath noise and vibrato:

const breath = slider(0.03, 0, 0.1)
const vibSpeed = slider(5, 3, 8)
const vibDepth = slider(0.3, 0, 0.5)
s("triangle").note("c5")
.attack(.1).decay(.1).sustain(.7).release(.2)
.noise(breath)
.vib(vibSpeed).vibmod(vibDepth)
.room(.4)._scope()

Percussion (Snare-like)

Noise with pitch envelope and short decay:

s("white")
.note("c4")
.penv(-12).pdecay(.05)
.decay(.15).sustain(0)
.hpf(200)
.room(.2)._scope()

Quick Reference: Acoustic Imitations

InstrumentBaseKey Settings
Stringssawtoothattack(.3), lpf(2000), vib(5)
Brasssawtoothlpf(400), lpenv(5), fast lpattack
Pianosawtoothfast attack, decay(.6), lpenv
Flutetriangle/sinenoise(.03), vib(5), vibmod(.3)
Percussionwhite noisepenv, short decay, hpf

Genre-Specific Textures

Dubstep Wobble (Detailed Walkthrough)

The wobble is an LFO controlling the filter cutoff:

Step 1: Bass tone

s("sawtooth").note("c1").lpf(400).room(.2)._scope()

Step 2: Use a signal to modulate filter

s("sawtooth").note("c1")
.lpf(sine.range(200, 2000).slow(2))
.lpq(5)
.room(.2)._scope()

Step 3: Speed up the wobble

s("sawtooth").note("c1")
.lpf(sine.range(200, 2000).fast(2))
.lpq(8)
.room(.2)._scope()

Step 4: Shape the wobble

const speed = slider(2, 0.5, 8)
const res = slider(10, 0, 20)
const dist = slider(0.3, 0, 1)
s("sawtooth").note("c1")
.lpf(sine.range(200, 2000).fast(speed))
.lpq(res)
.distort(dist)
.room(.2)._scope()

Lo-fi Textures

Bit crushing and sample rate reduction add grit:

const cutoff = slider(1200, 400, 3000)
const bits = slider(8, 2, 16)
const coarseAmt = slider(1, 1, 16)
s("sawtooth").note("c4 e4 g4 e4")
.lpf(cutoff)
.crush(bits)
.coarse(coarseAmt)
.room(.3)._scope()

Ambient Pad (Detailed Walkthrough)

Slow attack, long release, lots of reverb:

Step 1: Supersaw for thickness

s("supersaw").note("c4").room(.3)._scope()

Step 2: Slow envelope

s("supersaw").note("c4")
.attack(.8).decay(.3).sustain(.7).release(1.5)
.room(.3)._scope()

Step 3: Filter and reverb

s("supersaw").note("c4")
.attack(.8).decay(.3).sustain(.6).release(2)
.lpf(1500)
.room(.6)._scope()

Ambient chord pad โ€” shape the atmosphere:

const att = slider(1, 0.2, 2)
const rel = slider(2, 0.5, 4)
const cutoff = slider(1200, 400, 3000)
const reverb = slider(0.7, 0.2, 1)
chord("<C^7 Am7 F^7 G7>").voicing()
.s("supersaw")
.attack(att).decay(.3).sustain(.5).release(rel)
.lpf(cutoff)
.room(reverb)
.slow(2)._scope()

Risers & FX

Pitch sweep + filter sweep + noise = tension builder:

s("sawtooth")
.note("c2")
.lpf(sine.range(200, 4000).slow(4))
.penv(-24).pdecay(4)
.decay(4).sustain(0)
.room(.4)._scope()

White noise riser:

s("white")
.lpf(sine.range(500, 8000).slow(4))
.decay(4).sustain(0)
.room(.4)._scope()

Quick Reference: Genre Textures

TextureBaseKey Settings
Dubstep Wobblesawtoothlpf(sine.range(โ€ฆ)), lpq(10), distort
Lo-fianycrush(8), coarse(4), lpf
Ambient Padsupersawattack(.8), release(2), room(.6)
Risersaw/noiseslow filter sweep, penv

FM Synthesis

FM (Frequency Modulation) creates complex, often metallic timbres by using one oscillator to modulate another:

Basic FM

fm controls modulation amount, fmh sets the harmonic ratio:

const fmAmt = slider(2, 0, 12)
const fmRatio = slider(2, 1, 8)
s("sine").note("c4").fm(fmAmt).fmh(fmRatio).room(.3)._scope()

Bell Tones

Bells use non-integer harmonic ratios:

const fmAmt = slider(4, 1, 8)
const fmRatio = slider(2.4, 1.5, 4)
const dec = slider(0.8, 0.3, 2)
s("sine").note("c5")
.fm(fmAmt).fmh(fmRatio)
.decay(dec).sustain(0)
.room(.4)._scope()

Electric Piano

Classic e-piano uses FM with envelope on the modulation:

const fmAmt = slider(2, 0, 5)
const fmDec = slider(0.3, 0.05, 0.8)
s("sine").note("c4 e4 g4 c5")
.fm(fmAmt).fmh(2)
.fmdecay(fmDec)
.decay(.5).sustain(.2).release(.3)
.room(.3)._scope()

Metallic & Industrial

High FM amounts create harsh, industrial tones:

s("sine").note("c2")
.fm(6).fmh(7)
.decay(.3).sustain(0)
.room(.2)._scope()

Quick Reference: FM Synthesis

SoundfmfmhNotes
Clean0-Pure sine
Subtle harmonics1-22Slight color
Bell3-52.4+Non-integer ratio
E-piano22Add fmdecay
Metallic6+3+Harsh, industrial

Wavetables

Wavetables let you morph between different waveforms. Strudel includes the AKWF library with 1000+ waveforms:

Using Wavetables

Prefix with wt_ to load wavetables:

s("wt_piano").note("c4 e4 g4 c5").room(.3)._scope()
s("wt_saw").note("c4").room(.3)._scope()
s("wt_eguitar").note("c4 e4 g4 e4").room(.3)._scope()

Wavetable Position

Some wavetables have multiple waves you can sweep through with wt:

const pos = slider(0.5, 0, 1)
s("wt_dbass").note("c2")
.wt(pos)
.room(.3)._scope()

Warp Modes

Transform wavetables with different warp algorithms:

const warpAmt = slider(0.5, 0, 1)
s("wt_saw").note("c4")
.warpmode("FOLD")
.warp(warpAmt)
.room(.3)._scope()
const warpAmt = slider(0.5, 0, 1)
s("wt_saw").note("c4")
.warpmode("SYNC")
.warp(warpAmt)
.room(.3)._scope()

Quick Reference: Wavetable Warp Modes

ModeEffect
FOLDWave folding distortion
SYNCOscillator sync effect
PWMPulse width modulation
MIRRORSymmetric reflection
QUANTQuantization/bit reduction

Effects for Timbre

Distortion

Adds harmonics and grit:

const dist = slider(0.5, 0, 3)
const cutoff = slider(2000, 500, 8000)
s("sine").note("c3")
.distort(dist)
.lpf(cutoff)
.room(.2)._scope()

Shape (Waveshaping)

Subtler distortion:

const shapeAmt = slider(0.5, 0, 1)
s("sine").note("c3")
.shape(shapeAmt)
.room(.2)._scope()

Phaser

Sweeping notch filter for movement:

const speed = slider(2, 0.5, 8)
const depth = slider(0.8, 0, 1)
s("sawtooth").note("c4")
.lpf(2000)
.phaser(speed).phaserdepth(depth)
.room(.3)._scope()

Combining Everything

Hereโ€™s a complex sound using multiple techniques:

const cutoff = slider(800, 200, 2000)
const res = slider(5, 0, 15)
const dist = slider(0.2, 0, 1)
const vibSpeed = slider(4, 2, 8)
s("sawtooth").note("c3")
.attack(.05).decay(.2).sustain(.6).release(.3)
.lpf(cutoff).lpq(res).lpenv(4)
.lpattack(.01).lpdecay(.2).lpsustain(.4)
.vib(vibSpeed).vibmod(.1)
.distort(dist)
.room(.4)._scope()

Sound Design Recipes

Quick Reference Table

SoundRecipe
808 Kicks("sine").note("c1").penv(12).pdecay(.08).decay(.5).sustain(0)
Acid Basss("sawtooth").note("c2").lpf(200).lpq(15).lpenv(6).lpdecay(.15)
Stringss("sawtooth").attack(.3).lpf(2000).vib(5).vibmod(.2)
Brasss("sawtooth").lpf(400).lpenv(5).lpattack(.05)
Plucks("sawtooth").decay(.2).sustain(0).lpf(500).lpenv(4)
Bells("sine").fm(4).fmh(2.4).decay(.8).sustain(0)
Wobbles("sawtooth").lpf(sine.range(200, 2000).fast(2)).lpq(10)
Pads("supersaw").attack(.8).release(2).lpf(1200).room(.6)
Lo-fis("sawtooth").lpf(900).crush(6).coarse(4)
Subs("sine").note("c1").decay(.3).sustain(.5)

What You Learned

  • Waveforms determine the basic harmonic content: sine (pure), saw (bright), square (hollow), triangle (mellow)
  • Partials let you design custom waveforms with additive synthesis
  • ADSR envelopes shape how sounds evolve over time
  • Filter envelopes create dynamic timbral movement (wah, plucks, brass)
  • Pitch envelopes are essential for kicks, zaps, and basses
  • Filters (lpf, hpf) sculpt frequency content; resonance (lpq) adds character
  • FM synthesis creates metallic, bell-like, and complex tones
  • Wavetables provide ready-made complex waveforms with morphing
  • Effects (distort, crush, phaser) add further timbral options
  • Combine these tools to recreate classic synth sounds and acoustic instruments