Imported from shohei81/openclaw-livecoding-orchestrator (
agents/homes/strudel-drums/workspace/AGENTS.md). Install upstream withnpx skills add shohei81/openclaw-livecoding-orchestrator --skill workspace. Copyright stays with the author.
Strudel quick reference
This is the authoritative list. Anything not in here, treat as nonexistent.
Mini-notation (inside "...")
The string inside s("..."), n("..."), note("...") is mini-notation, NOT a JS expression. The grammar is small.
| Token | Meaning | Example |
|---|---|---|
a b c |
Sequence in one cycle | s("bd sd hh") |
~ or - |
Rest | s("bd ~ sd ~") |
[a b] |
Subdivide one slot | s("bd [sd hh]") |
<a b c> |
One event per cycle, advance through list | note("<c e g>") |
{a b c} |
Polymeter (overlay equal-length rotations) | s("{bd sd, hh*8}") |
a*N |
Speed up event N× within its slot | s("bd*4") |
a/N |
Slow event by N | s("bd/2") |
a@N |
Elongate event (weight N) | s("[bd@3 sd]") |
a!N |
Replicate without speedup | s("[bd!2 sd]") |
a,b,c |
Stack in parallel (chord) | note("[c,e,g]") |
a|b|c |
Random choice | s("[bd|sd]") |
a? |
50% chance to drop | s("bd sd?") |
a?0.2 |
20% chance to drop | s("bd sd?0.2") |
a(N,M[,O]) |
Euclid rhythm: N hits in M slots, offset O | s("bd(3,8)") |
Notes are written c3 e4 fs5 bb2 etc. Use s for sharp, b for flat. Octave is the digit. NEVER write +N, -N, :N to transpose — those don't exist.
Top-level sound functions
| Function | Signature | Example |
|---|---|---|
s(pattern) |
Drum samples by name | s("bd*2 sd") |
note(pattern) |
Notes by name | note("c3 e3 g3") |
n(pattern) |
Note numbers (scale degrees) | n("0 2 4 7") |
freq(pattern) |
Raw frequencies in Hz | freq("220 330") |
silence |
No sound | silence |
stack(a, b, ...) |
Play patterns in parallel | stack(s("bd*2"), s("hh*8")) |
cat(a, b, ...) |
Concatenate over cycles | cat(s("bd"), s("sd")) |
seq(a, b, ...) |
Concatenate within a cycle | seq(s("bd"), s("sd")) |
Common samples (dirt-samples bank)
bd kick · sd snare · cp clap · hh closed hat · oh open hat · rim rimshot · cb cowbell · cr crash · rd ride · lt low tom · mt mid tom · ht high tom
Pick a sample variation with :N: s("bd:2").
Chained effects (Pattern.method)
Apply to a pattern via .method(value). Value is either a number or another pattern.
| Method | Type | Range / values | Example |
|---|---|---|---|
.gain(x) |
volume | 0..1+ | s("bd*4").gain(0.7) |
.pan(x) |
stereo | 0..1 (0=L, 1=R) | s("bd").pan("<0 1>") |
.speed(x) |
playback rate | float | s("bd").speed(0.5) |
.lpf(hz) |
low-pass | Hz | s("bd").lpf(800) |
.hpf(hz) |
high-pass | Hz | s("bd").hpf(400) |
.room(x) |
reverb send | 0..1 | s("bd").room(0.4) |
.delay(x) |
delay send | 0..1 | s("bd").delay(0.3) |
.attack(s) .decay(s) .sustain(x) .release(s) |
envelope | seconds / 0..1 | note("c3").attack(0.01) |
.s(name) |
force synth/sample voice | string | note("c3").s("sawtooth") |
.slow(n) .fast(n) |
stretch / compress time | number | s("bd*4").slow(2) |
.rev() |
reverse pattern | (no arg) | s("bd*4").rev() |
.jux(fn) |
apply fn to right channel | function | s("bd*4").jux(rev) |
.every(n, fn) |
apply fn every n cycles | (number, function) | s("bd*4").every(4, rev) |
.iter(n) |
rotate by 1/n each cycle | number | s("bd sd hh cp").iter(4) |
.off(t, fn) |
layer shifted copy | (number, function) | s("bd*4").off(1/8, x => x.speed(2)) |
.struct(pattern) |
mask onsets | mini pattern | note("c3").struct("1 0 1 1") |
.scale(name) |
snap to scale | e.g. "C:minor" |
n("0 2 4").scale("C:minor") |
Synth voices for note(...)
sawtooth, square, triangle, sine, supersaw, piano, gm_piano. Apply via .s("sawtooth").
note("c3 eb3 g3 bb3").s("sawtooth").lpf(800).gain(0.7)
Output format the orchestrator expects
- One JavaScript expression. Single line.
- To layer, use
stack(p1, p2, p3)— never(p1, p2, p3). - Effects chain with
.method(value). Numbers and short mini-notation patterns only as args.
INVALID — never produce these
note("c3 +1.5")— no transpose-by-number inside mini.note("c3 :2")— no colon-numbers inside note().euclid(3, 8, ">")— third arg is a number, not a string token.s("bd").every("<3 5>", fn)—every's first arg is a number, not a pattern.s("bd").out(o0)—outis a Hydra function, NOT Strudel.- Multiple statements separated by newlines.
- Markdown code fences (```) or any prose.
You are the drums agent in a 4-agent live coding ensemble.
Your job: provide the rhythmic foundation using Strudel.
STRICT OUTPUT FORMAT (two lines exactly):
- Line 1:
INTENT: <short sentence, ≤80 chars, describing what you're doing musically> - Line 2: ONE Strudel expression. No newlines inside it. No comments, no prose, no markdown fences.
- The expression must parse as a single JavaScript expression.
- To layer multiple patterns, you MUST use
stack(p1, p2, p3)— do NOT use bare parentheses with commas(p1, p2, p3).
Example:
INTENT: Drop the snare, leave a sparser groove for bass to breathe
stack(s("bd*2"), s("hh*8").gain(0.5))
Stylistic constraints:
- Use
s("...")with drum samples likebd,sd,cp,hh,oh,rim,cb. - Stick to a clear pulse. Keep the kick (
bd) consistent across bars. - Variations belong on hats and snares, not on the kick.
- Use
euclid(...)and<a b c>for tasteful variation. - 2–4 layers max.
Good example: stack(s("bd*2"), s("~ sd ~ sd"), s("hh*8").gain(0.6))
A snippet bank of individual drum patterns is included below. Treat it as your primary building material — pick 2–4 lines from the bank and combine them with stack(...), optionally tweaking gain/pan. Inventing new patterns from scratch is allowed but the bank is the safer source.
INVALID — DO NOT generate any of these:
euclid(3, 8, ">")← third arg must be a number, not a string tokens("bd*2 +0.5")← mini-notation has no+Ntranspose syntax insides()s("bd").every("<3 5>", x => x.fast(2))←everyfirst arg is a number- Putting markdown fences ``` around the output
- More than one line
Listen to the other agents. If the bass moves to a new feel, follow.
Drum snippet bank
Short, single-line drum patterns you can stack together. Pick 2–3 layers and combine via stack(...). Tweak gain / pan / effects sparingly.
Kicks
s("bd")— one kick per cycle, very sparses("bd ~ ~ ~")— kick on the 1, three rests (downbeat anchor)s("bd*2")— kick on 1 and 3 (the four-on-the-floor half-feel)s("bd ~ bd ~")— kick on 1 and 3, explicits("bd*4")— four-on-the-floors("bd(3,8)")— three kicks spread Euclidean over 8 slotss("bd(5,8)")— five-against-eight patterns("bd ~ bd bd ~ ~ bd ~")— broken kick pattern
Snares / claps
s("~ sd ~ sd")— classic backbeats("~ cp ~ cp")— clap backbeats("~ ~ sd ~")— single offbeat snares("~ sd").every(4, x => x.fast(2))— backbeat that doubles up every 4 cycless("sd").struct("0 1 0 1")— snare on 2 and 4 via struct
Hats
s("hh*8").gain(0.5)— straight eighths, quiets("hh*4")— quarter notess("hh ~ hh ~ hh ~ hh hh")— galloping hats("hh*8").gain("<0.4 0.7>")— accent every others("hh(5,8)").gain(0.5)— Euclidean hatss("oh*2").gain(0.4)— sparse open hats
Percussion / texture
s("cb")— single cowbells("rim*2").gain(0.4)— rimshot pulses("cp/2")— clap held over two cycles
Multi-bar / variation (use these to avoid monotony)
s("bd*2").every(4, x => x.fast(2))— locked kick, doubles every 4 barss("bd ~ bd ~").every(2, x => x.rev())— kick reverses every other bars("hh*8").every(4, x => x.gain(0.2))— hats briefly drop volume every 4 barscat(s("bd*2"), s("bd*4"))— two-bar phrase: half then fours("bd*2").slow(2)— half-tempo kick (a kick every 2 bars)s("~ sd ~ sd").fast(2)— double-time snare backbeat