Strudel is a live coding instrument in your browser. Type a pattern, press play, and sound comes out. No install. Runs in a tab.
Every code block below is a live editor. Change anything, hit the play button, hear what breaks. That’s the whole method.
Play the code first. Listen. Then read the text around it. The sound comes before the name. Always.
Hit play.
A kick drum. s() plays a sample. "bd" is that sample’s name: bass drum.
Swap bd for sd (snare), hh (hi-hat), cp (clap),
or oh (open hat). Hit play after each change.
Now line up a few:
Four sounds, evenly spaced. Strudel carves one cycle into equal slots. More sounds, less time each. Fewer sounds, more time each.
Pack it: s("bd sd hh oh cp mt lt ht"). Then strip it back to two. Hear the timing shift.
What you just played is a pattern: events spread across a repeating cycle. Sounds, notes, effects: everything in strudel is a pattern. The cycle is the clock.
The text inside the quotes is mini-notation, a tiny language for shaping patterns. Five tools.
Cram two sounds where one used to be. [ ] splits a slot’s time between everything inside.
Nest them: s("bd [sd [hh hh]] oh cp"). Brackets inside brackets. Each level subdivides further.
*8 crams the sound in eight times, all within one cycle. Hi-hat rolls come from this.
Tilde ~ holds a slot open but plays nothing. Without gaps, everything runs together. The rest gives each hit space.
(3,8) distributes three kicks as evenly as possible across eight slots.
The math picks the spacing. It ends up sounding good because even distribution tends to.
Try (5,8), (3,4), (7,16), (2,5).
Different numbers, different feel. Some of these are West African and Afro-Cuban rhythms, centuries old.
Hear that pattern you just played? That’s the tresillo. Three hits, eight slots, spaced as evenly as possible.
What you’re looking at is a rhythm necklace: a circular pattern where rotation doesn’t change the shape, only where beat 1 falls. Every row in the table above is a different necklace. The tresillo and the son clave are the same necklace, worn differently.
Distribute 3 hits across 8 slots as evenly as possible. The gaps between hits can only ever be two sizes (here, 3 and 2). That’s maximal evenness.
[x . . x . . x .]
| pattern | name | tradition | vibe |
|---|---|---|---|
| (3,8) | Tresillo | Cuban / West African | The backbone of Afro-Cuban music. Claves, cowbell, bass. |
| (5,8) | Cinquillo | Cuban contradanza | Tresillo’s denser cousin. Timbales, guiro. |
| (3,4) | Cumbia | Colombian / Persian | Nearly full: three hits, one gap. |
| (7,12) | West African bell | Ashanti / Yoruba / Ewe | The standard bell pattern. Everything else plays around it. |
| (5,16) | Bossa nova | Brazilian | Two bars of floating syncopation. |
| (7,16) | Samba | Brazilian | Dense, driving agogo bell pattern. |
(3,8,2) is the same three hits, started from a different position. Same necklace, different downbeat. The tresillo and the son clave are rotations of each other. Same math, different groove.
< > rotates through its options, one per cycle. Snare, clap, snare, clap.
[ ] subdivide • *N multiply • ~ rest •
(k,n) euclidean • < > alternate
Five tools. Every rhythm you’ve heard uses some combination of these.
One pattern at a time gets old. Music is layers.
Kick on 1 and 3. Clap on 2 and 4. Hi-hats throughout. You just built a drum beat.
Swap the hat for s("hh*16"). Make the kick euclidean: s("bd(3,8)").
Stack as many layers as you want.
Now add pitch. note() plays notes:
C in the second octave. .s("sawtooth") swaps the sample for a synthesizer, a raw buzzy wave.
.lpf(500) chops the harsh highs. .decay() and .sustain(0) make it short and punchy.
s() plays a recording. Someone hit a drum, we play it back. note() generates a wave from scratch. No recording, just math. Samples sound “real.” Synths sound “electronic.” Both are patterns.
| wave | sound | code |
|---|---|---|
| sine | pure, clean, sub-bass | .s("sine") |
| sawtooth | bright, buzzy, leads + bass | .s("sawtooth") |
| square | hollow, woody, chiptune | .s("square") |
| triangle | soft, muted, gentler saw | .s("triangle") |
A filter removes frequencies. .lpf(500) = low-pass filter at 500hz. Everything above 500hz gets cut. The number is the cutoff. Lower number = darker sound. Higher = brighter.
| filter | does | code |
|---|---|---|
| lpf | cuts highs (low-pass) | .lpf(500) |
| hpf | cuts lows (high-pass) | .hpf(200) |
An envelope shapes a sound over time. Four stages:
| stage | controls | code |
|---|---|---|
| attack | how fast the sound starts | .attack(0.01) |
| decay | how fast it drops from peak | .decay(0.1) |
| sustain | level it holds at (0–1) | .sustain(0.5) |
| release | how fast it fades after stopping | .release(0.3) |
.sustain(0) with a short .decay() = a pluck. No sustain, instant drop.
Chain effects with dots: note("c2").s("sawtooth").lpf(500).
Each one adds something: sound, filter, envelope. Order barely matters.
Everything from this lesson, combined. Play it, wreck it, rebuild it until it sounds like yours. Then hit share.
note("<c2 [c2 eb2] f2 [eb2 c2]>")s("cp(3,8)").gain(.4)s("<hh*8 hh*16>")| tool | does | looks like |
|---|---|---|
| s() | plays a sample by name | s("bd sd hh") |
| note() | plays a pitched note through a synth | note("c2 eb2") |
| stack() | layers patterns on top of each other | stack(s("bd"), s("hh*8")) |
| [ ] | subdivides a slot (crams sounds in) | s("bd [sd sd]") |
| *N | repeats a sound N times in one slot | s("hh*8") |
| ~ | rest — holds the slot silent | s("bd ~ sd ~") |
| (k,n) | euclidean — distributes k hits across n slots | s("bd(3,8)") |
| < > | alternates options, one per cycle | s("bd <sd cp>") |
| .method() | chains effects onto a pattern | .lpf(500).gain(.5) |
| .s() | sets the sound source (sample or synth type) | .s("sawtooth") |
| .gain() | sets volume (0 to 1+) | .gain(.5) |
| .lpf() | low-pass filter — cuts frequencies above cutoff | .lpf(500) |
| .decay() | how fast the sound drops from peak | .decay(.15) |
| .sustain() | level the sound holds at after decay | .sustain(0) |
| sawtooth | a bright, buzzy synthesizer wave | .s("sawtooth") |
Next: The Break — 170bpm, half-time snare, an actual DnB beat.
| syntax | name | does | example |
|---|---|---|---|
| [ ] | brackets | subdivides a slot | s("bd [sd sd]") |
| *N | multiply | repeats N times in one slot | s("hh*8") |
| ~ | rest | silence, holds the slot | s("bd ~ sd ~") |
| (k,n) | euclidean | distributes k hits across n slots | s("bd(3,8)") |
| (k,n,r) | euclidean + rotation | same but rotated r steps | s("bd(3,8,2)") |
| < > | alternate | one option per cycle, rotating | s("bd <sd cp>") |
| /N | slow | stretches pattern across N cycles | s("[bd sd hh oh]/2") |
| !N | replicate | repeats without changing speed | s("bd!3 sd") |
| ? | degrade | 50% chance of silence | s("hh*8?") |