Strudel: browser groove starter
Learning objectives
- learner can trigger drum and pitched sounds with sound()/note() inside the cycle model
- learner can write mini-notation rhythms and layer parallel patterns
- learner can set tempo and stack independent voices into a groove
Capstone — one whole task that evidences the objectives
Build a 4-voice Strudel groove at strudel.cc — drums via bank(), a bassline via note(), layered with $:/stack and set with setcpm — starting from a blank editor.
Prerequisite modules
This module builds the foundational live-coding act: opening strudel.cc in any browser — no install, no audio interface, no setup — and typing a multi-voice electronic groove into existence from an empty editor. That whole task is the daily bread of a browser-rig performer: at an algorave or a bedroom stream, the set starts from a blank page and grows one line at a time, house- or techno-flavoured, with drum-machine kits (808, 909) driving the pulse.
The arc scaffolds toward that. First, internalize the cycle as the unit of time — Strudel spaces every element evenly within one fixed-length cycle, so more sounds means denser, not longer. From there, drill the two trigger verbs: playing named samples with sound() and its colon variants, and pitching a line with note() using MIDI numbers or letter names. The standard two-letter drum abbreviations (bd, sd, hh) give you an idiomatic drum vocabulary immediately, and mini-notation — brackets, angle brackets, rests, *N repeats — turns a flat string into real rhythm. Repointing those abbreviations with bank() swaps a whole kit’s timbre without touching the rhythm you wrote.
The final assembly is the unsupported capstone: four independent voices declared with the $: prefix (or merged with stack()), tempo pinned with setcpm — every required atom is something the groove literally cannot be built without. Supporting atoms enrich the picture: orienting context on Strudel’s browser-native origins and no-install premise, microtonal pitch detail and the alternative mininotation formulation for reference, effect chains like .lpf and .gain for polish, the cat/seq/stack factory trio for later compositional range, Strudel’s lineage from TidalCycles, and the first-play sample-loading quirk that saves a newcomer from thinking their correct pattern is broken.
Walkthrough
Open strudel.cc — nothing to install. Type each block, hit Ctrl-Enter (Cmd-Enter on Mac) to play, and Ctrl-. to stop. We build the capstone groove one voice at a time; each step is a complete, playable program that adds one idea to the last.
1 — a pulse. First set the tempo, then put a kick on every beat. setcpm(120/4) means 120 BPM in 4/4 (one bar per cycle); bd*4 is the kick abbreviation repeated four times, evenly spaced across the cycle.
setcpm(120/4)
$: sound("bd*4")
2 — backbeat and hats. Add two more voices with their own $: line — each $: is an independent, separately-editable track. ~ is a rest, so ~ sd ~ sd puts the snare on beats 2 and 4; hh*8 gives eighth-note hats.
setcpm(120/4)
$: sound("bd*4")
$: sound("~ sd ~ sd")
$: sound("hh*8")
3 — pick a kit. The rhythm is right but the sounds are generic. .bank("RolandTR909") repoints those same abbreviations at a 909 kit — the timbre changes, the pattern you wrote does not.
setcpm(120/4)
$: sound("bd*4").bank("RolandTR909")
$: sound("~ sd ~ sd").bank("RolandTR909")
$: sound("hh*8").bank("RolandTR909")
4 — a bassline. Drums alone don’t groove. note() plays pitches (letter names with octave, ~ still a rest); .sound("sawtooth") picks a synth voice to play them through. The root on the downbeat, a b3 and a 5th give it a minor, driving feel.
setcpm(120/4)
$: sound("bd*4").bank("RolandTR909")
$: sound("~ sd ~ sd").bank("RolandTR909")
$: sound("hh*8").bank("RolandTR909")
$: note("c2 ~ eb2 g2").sound("sawtooth")
5 — mix and shape (the capstone). Two chainable effects finish it: .gain() balances the levels (the raw hats are too loud, the kick a touch hot), and .lpf(800) rolls the top off the bass so it sits under the kick instead of fighting it. This is the four-voice groove the capstone asks for:
setcpm(120/4)
$: sound("bd*4").bank("RolandTR909").gain(0.9)
$: sound("~ sd ~ sd").bank("RolandTR909")
$: sound("hh*8").bank("RolandTR909").gain(0.4)
$: note("c2 ~ eb2 g2").sound("sawtooth").lpf(800)
The stack() form. $: is the live-coding shorthand; stack() is the same four voices as one expression — useful when you want to transform the whole groove at once (e.g. .slow(2), .rev()).
setcpm(120/4)
stack(
sound("bd*4").bank("RolandTR909").gain(0.9),
sound("~ sd ~ sd").bank("RolandTR909"),
sound("hh*8").bank("RolandTR909").gain(0.4),
note("c2 ~ eb2 g2").sound("sawtooth").lpf(800)
)
Now make it yours. Swap RolandTR909 for RolandTR808; change the bass notes; try hh*16 or [hh hh]*4 for a busier hat; add .room(0.3) to the snare. Every change is one line, re-evaluated live — which is the whole job.
Atoms in this module
Required — these gate the capstone
Supporting — enrichment, not gating
Part of curricula
- Live Coder — zero to performing live-coded music — First Sounds in the Browser required
Unlocks — modules that require this one