What is Live Coding?
Live coding is the practice of writing code in real-time to generate music, visuals, or both — often in front of a live audience. It sits at the intersection of programming, improvisation, and performance art. You write a line of code, hit run, and the sound changes instantly. No DAW. No piano roll. Just you and a text editor making beats.
The live coding scene has been growing for years through communities like Algorave and tools like TidalCycles (Haskell), Sonic Pi (Ruby), and FoxDot (Python). But the one that's caught my attention recently is Strudel — because it runs entirely in the browser.
Why Strudel?
Strudel is a JavaScript port of TidalCycles, the legendary Haskell live coding environment created by Alex McLean. What makes Strudel special:
- Zero setup — runs entirely in the browser, no installation needed
- JavaScript-based — if you know JS, you already know the syntax
- Massive sound library — hundreds of samples and synths built in
- Embeddable — you can embed interactive patterns in any webpage (like this blog post)
- Pattern-based — uses a powerful mini-notation for describing rhythmic patterns
Every code block below is an embedded Strudel REPL. Click play to hear them live.
Step 1: Your First Beat
Let's start with the absolute basics. In Strudel, s() triggers a sample. The string inside defines the pattern — each word is a beat:
s("bd sd bd sd")
This plays: kick, snare, kick, snare. Simple four-on-the-floor. Try it:
bd = bass drum, sd = snare drum. Strudel has hundreds of sample names you can explore.
Step 2: Mini-Notation Magic
Strudel's power comes from its mini-notation — a compact syntax for describing complex rhythms. Here are the key operators:
[x y]— subdivide: fit multiple events into one stepx*n— repeat: play something n times in its time slot~— rest: silence for that beatx:n— sample variant: pick a different version of the sound
Let's use these to make something that actually grooves. We'll use the Roland TR-808 sample bank:
s("bd sd:2 [bd bd] sd:2").bank("RolandTR808")
The [bd bd] crams two kicks into one beat — creating a syncopated double-hit. sd:2 picks the second snare variant. .bank("RolandTR808") loads the iconic 808 sounds:
Step 3: Layering with Stack
stack() lets you layer multiple patterns on top of each other — like tracks in a DAW, but written in a single expression. Let's add hi-hats on top of our drum pattern:
stack(
s("bd sd:2 [bd bd] sd:2").bank("RolandTR808"),
s("hh*8").bank("RolandTR808").gain(0.4)
)
hh*8 repeats the hi-hat 8 times per cycle — giving us steady eighth notes. .gain(0.4) turns them down so they don't overpower the kick and snare:
Step 4: Adding a Bassline
Drums are only half the story. note() lets you write melodic patterns using note names. Combined with .sound() to pick a synth and .cutoff() for filtering, you can build basslines right alongside your drums:
stack(
s("bd*2 sd:3 [~ bd] sd:3").bank("RolandTR808"),
s("hh*8").bank("RolandTR808").gain(0.3),
note("<c2 [eb2 g2] c2 [bb1 c2]>")
.sound("sawtooth").cutoff(900).gain(0.3)
)
The < > angle brackets create a slow pattern — the notes alternate across cycles rather than playing within a single cycle. The sawtooth wave with a low cutoff gives us that thick, filtered bass sound:
Step 5: The Full Beat
Now let's put it all together. This is a full layered track built from scratch — 808 drums with a syncopated groove, hi-hats, an open hat for accents, a growling sawtooth bass with an LFO on the filter, and a delayed square lead floating on top:
stack(
s("bd:5*2 sd:3 [~ bd:5] sd:3")
.bank("RolandTR808").gain(0.8),
s("[~ hh]*4")
.bank("RolandTR808").gain(0.3),
s("~ ~ ~ oh")
.bank("RolandTR808").gain(0.25),
note("<c2 c2 eb2 [bb1 c2]>")
.sound("sawtooth")
.cutoff(sine.range(400,1200))
.resonance(8).gain(0.35),
note("c4 [eb4 g4] ~ bb4")
.sound("square").cutoff(2000)
.delay(0.4).gain(0.12).room(0.4)
).cpm(70)
Let's break down what makes this tick:
- Drums —
bd:5picks a deep 808 kick variant,[~ bd:5]creates a ghost kick on the off-beat - Hi-hats —
[~ hh]*4plays hats only on the off-beats (the~creates rests on the downbeats) - Open hat — only on beat 4 for that classic accent
- Bass —
sine.range(400,1200)modulates the filter cutoff with a sine wave LFO, making the bass breathe - Lead — a square wave with
.delay(0.4)for echo and.room(0.4)for reverb, giving it space .cpm(70)— sets the tempo to 70 cycles per minute
Hit play:
Where to Go From Here
This barely scratches the surface. Strudel supports:
- Euclidean rhythms —
s("bd").euclid(3,8)generates mathematically distributed beats - Random variation —
.sometimes(x => x.speed(2))for controlled chaos - Audio effects — reverb, delay, distortion, phaser, chorus, and more
- Custom samples — load your own sounds via URL
- MIDI output — control hardware synths directly from the browser
- Visual patterns — built-in visualizations that react to the music
The best part about live coding is that the code is the instrument. Every character you type changes the music. There's no compile step, no render time — just immediate sonic feedback. It's the closest programming gets to playing a musical instrument.
Open strudel.cc, start typing, and see what comes out. The worst that can happen is a weird noise — and sometimes that's the best part.
Code is music. Music is code.