Getting started
StrudelUI is a step sequencer for Strudel, a language for making music with code. You click steps on a grid, and it writes the Strudel code for you. Change the code, and the grid follows. You never need to type code to make a beat, but you'll learn it by watching.
This page covers what the grid does. For the whole language, every function and how to write it by hand, see Strudel's own docs and workshop; each lesson below links the part that goes deeper.
Every example on this page is real Strudel code. Press ▶ play to hear it (or ctrl+enter inside the code), and edit it while it plays: the sound and the little grid under it update as you type. open in StrudelUI takes the example into the app.
setcpm(120/4)
const kick = s("bd").bank("RolandTR909")
.struct("x ~ ~ ~ x ~ ~ ~ x ~ ~ ~ x ~ ~ ~")
stack(
kick,
)
That's a kick drum on every beat. Three lines do the work:
setcpm(120/4)sets the tempo: 120 beats per minute, 4 beats to a bar.const kick = …is a track.s("bd")picks the sound (a bass drum),.bank("RolandTR909")picks which drum machine it comes from.stack(kick)at the end is the mix: the tracks that play together.
Try it: in the code above, change one ~ to an x and listen.
Steps and rhythm
A bar has 16 steps, four per beat. In .struct("…"), x is a hit and ~ is a rest. The extra spaces between groups of four are only there to make the beats easy to see.
setcpm(124/4)
const kick = s("bd").bank("RolandTR909")
.struct("x ~ ~ ~ x ~ ~ ~ x ~ ~ ~ x ~ ~ ~")
const hats = s("hh").bank("RolandTR909")
.struct("~ ~ x ~ ~ ~ x ~ ~ ~ x ~ ~ ~ x ~")
const clap = s("cp").bank("RolandTR909")
.struct("~ ~ ~ ~ x ~ ~ ~ ~ ~ ~ ~ x ~ ~ ~")
stack(
kick,
hats,
clap,
)
Kick on every beat, hats on the off-beats, a clap on beats 2 and 4: the classic house groove. In the app you'd click those steps on the grid instead.
Strudel has shorthands for repeating patterns. [ ] groups steps and * repeats the group, so "[x x]*8" is sixteen hits and "[~ x]*4" is a hit on every second eighth. StrudelUI reads these and shows them on the grid. It keeps your notation until you edit that row, then writes it out step by step.
setcpm(124/4)
const kick = s("bd").bank("RolandTR909")
.struct("[x ~ ~ ~]*4")
const hats = s("hh").bank("RolandTR909")
.struct("[x x]*8")
.gain(.4)
stack(
kick,
hats,
)
Full docs: mini-notation ↗
Sounds and kits
Strudel comes with thousands of sounds: classic drum machines, sampled instruments and synths. Drum sounds have short names: bd bass drum, sd snare, cp clap, hh closed hat, oh open hat, rd ride, lt mt ht toms.
.bank("RolandTR808")picks the drum machine. Swap 909 for 808 below and the whole beat changes character.s("bd:3")picks a variation: most sounds have several, numbered from 0.
setcpm(124/4)
const kick = s("bd:1").bank("RolandTR808")
.struct("x ~ ~ ~ x ~ ~ ~ x ~ ~ ~ x ~ x ~")
const snare = s("sd").bank("RolandTR808")
.struct("~ ~ ~ ~ x ~ ~ ~ ~ ~ ~ ~ x ~ ~ ~")
const hats = s("hh").bank("RolandTR808")
.struct("[x x]*8")
.gain(.35)
stack(
kick,
snare,
hats,
)
Your own sounds: + samples → a sounds folder… imports a folder of audio files, the same way strudel.cc's "import sounds folder" does. Each subfolder becomes a sound named after it, and its files are its variations, numbered from 0 in alphabetical order:
samples/
swoop/ swooplong.wav swoopshort.wav swooptight.wav → s("swoop:0") … s("swoop:2")
smash/ smashhigh.wav smashlow.wav smashmiddle.wav → s("smash:0") … s("smash:2")
You can also drop files or folders onto the page. They're kept in your browser for next time.
Full docs: samples ↗
Notes
For melodies and basslines, the pattern goes in note("…") instead of .struct: each step is a note name or a rest. A note is a letter and an octave, like c2 or g3; # is sharp and b is flat (f#2, eb2). Lower numbers are lower notes.
setcpm(124/4)
const kick = s("bd").bank("RolandTR909")
.struct("x ~ ~ ~ x ~ ~ ~ x ~ ~ ~ x ~ ~ ~")
const bass = note("~ ~ c2 ~ ~ ~ c2 ~ ~ ~ eb2 ~ ~ ~ g1 ~").s("sawtooth")
.decay(.15)
.sustain(0)
.lpf(600)
stack(
kick,
bass,
)
.s("sawtooth") plays the notes on a synth. Try "square", "triangle" or "supersaw". .decay and .sustain shape each note: here every note is a short pluck.
Full docs: notes ↗ · scales and chords ↗ · synths ↗
Mix and effects
Each line under a track is one setting. StrudelUI's sliders and effects panel just add or change these lines:
.gain(.8)volume (the gain slider).lpf(800)low-pass filter: lower numbers sound darker (the high cut slider).room(.5)reverb (the space slider).delay(.3),.distort(1),.crush(6),.pan(.2)and more in the fx panel
In the mix, a track with // in front is muted: it's commented out, so it doesn't play. Remove the // in front of hats below while it plays.
setcpm(126/4)
const kick = s("bd").bank("RolandTR909")
.struct("x ~ ~ ~ x ~ ~ ~ x ~ ~ ~ x ~ ~ ~")
.duckorbit(2)
.duckdepth(.6)
.orbit(1)
const pad = note("c3 ~ ~ ~ ~ ~ ~ ~ eb3 ~ ~ ~ ~ ~ ~ ~").s("supersaw")
.add(note("0,3,7"))
.lpf(1200)
.room(.6)
.gain(.4)
.orbit(2)
const hats = s("hh").bank("RolandTR909")
.struct("[~ x]*8")
.delay(.3)
.gain(.4)
.orbit(3)
stack(
kick,
pad,
// hats,
)
Two more ideas in there:
.add(note("0,3,7"))turns each pad note into a chord: the note itself, 3 semitones up and 7 up (a minor chord)..orbit(…)puts tracks on separate buses, and.duckorbit(2)makes the kick briefly turn bus 2 down on every hit. That's the "pump" you hear on the pad, and the pump slider in the app.
Soloing a track writes // solo next to it in the mix, so a solo is part of the code too.
Full docs: effects ↗
Bars, scenes and songs
So far every pattern is one bar, repeating. Put several bars in <[ … ] [ … ]> and Strudel plays one per bar, in turn. Tracks can have different lengths: the shorter ones loop underneath.
setcpm(124/4)
const kick = s("bd").bank("RolandTR909")
.struct("x ~ ~ ~ x ~ ~ ~ x ~ ~ ~ x ~ ~ ~")
const bass = note("<[~ ~ c2 ~ ~ ~ c2 ~ ~ ~ c2 ~ ~ ~ c2 ~] [~ ~ ab1 ~ ~ ~ ab1 ~ ~ ~ bb1 ~ ~ ~ bb1 ~]>").s("sawtooth")
.decay(.15)
.sustain(0)
.lpf(700)
stack(
kick,
bass,
)
A scene is a set of tracks that play together: const intro = stack(kick, hats). A song plays scenes in order for so many bars each, with arrange, and then starts over:
setcpm(124/4)
const kick = s("bd").bank("RolandTR909")
.struct("x ~ ~ ~ x ~ ~ ~ x ~ ~ ~ x ~ ~ ~")
const hats = s("hh").bank("RolandTR909")
.struct("[~ x]*8")
.gain(.4)
const bass = note("~ ~ c2 ~ ~ ~ c2 ~ ~ ~ eb2 ~ ~ ~ g1 ~").s("sawtooth")
.decay(.15)
.sustain(0)
.lpf(600)
const intro = stack(kick, hats)
const drop = stack(kick, hats, bass)
const song = arrange(
[2, intro],
[4, drop],
)
song
Full docs: time and structure ↗
Saving and exporting
Your tune autosaves in the browser as you work. To keep versions:
- save (or cmd/ctrl+s) keeps a named copy in this browser; the file menu (the tune's name, top left) opens and downloads them.
- export → code downloads the tune as a
.jsfile. It's plain Strudel, so it also runs on strudel.cc, or use open in strudel above the code. - export → mix renders a WAV, and stems renders one WAV per track (zipped) for a DAW, both faster than real time.
- ● rec records what you play live, scene changes and all.
Everything stays in your browser: clearing site data clears it, so download anything you want to keep.
Past the grid
The grid is a way into Strudel, not the edge of it. Any track the grid can't show as steps still plays: it shows up as a code track, its hits outlined as they play, and you edit it in the code panel. A few things to try:
setcpm(124/4)
const kick = s("bd").bank("RolandTR909")
.struct("x ~ ~ ~ x ~ ~ ~ x ~ ~ ~ x ~ ~ ~")
const riff = n("<0 2 4 7>(3,8)").scale("C3:minor").s("triangle")
.decay(.2)
.sustain(0)
.jux(rev)
.gain(.5)
stack(
kick,
riff,
)
(3,8)is a euclidean rhythm: 3 hits spread as evenly as possible over 8 steps.<0 2 4 7>takes a different note each bar, and.scale("C3:minor")turns the numbers into notes of a scale..jux(rev)plays the pattern backwards in the right ear.
From here, Strudel's own workshop and reference go much further. Everything there works in StrudelUI's code panel.