Arranging Patterns
Arranging is how you organize musical patterns over time and space. While individual patterns create rhythms and melodies, arranging combines them into complete pieces of music. This guide covers Strudelโs powerful tools for sequencing, layering, and structuring your compositions.
What is Arranging?
In traditional music production, arranging means deciding what plays whenโwhich instruments enter in the intro, how the verse differs from the chorus, when the drums drop out for a breakdown. In Strudel, we have functions that give you precise control over these decisions.
There are two fundamental dimensions to arranging:
- Time โ What plays in sequence (one after another)
- Space โ What plays simultaneously (layered together)
Letโs explore both.
Functions vs Mini Notation: The Rosetta Stone
Before diving in, hereโs the key insight: every arranging function has a mini notation equivalent. You can write the same patterns either way:
| What you want | Function | Mini Notation | Symbol |
|---|---|---|---|
| One per cycle (slow) | cat(a, b, c) | "<a b c>" | < > angle brackets |
| All in one cycle (fast) | seq(a, b, c) | "a b c" | space |
| Play together (layer) | stack(a, b, c) | "a,b,c" | , comma |
| Different lengths | polymeter([a,b,c],[x,y]) | "{a b c, x y}" | { } curly braces |
Side-by-side examples:
// Function: cat - one per cycle
cat(s("bd"), s("sd"), s("hh"))// Mini notation: <> - one per cycle
s("<bd sd hh>")// Function: seq - all in one cycle
seq(s("bd"), s("sd"), s("hh"), s("cp"))// Mini notation: space - all in one cycle
s("bd sd hh cp")// Function: stack - play together
stack(s("bd ~ bd ~"), s("~ sd ~ sd"), s("hh*4"))// Mini notation: comma - play together
s("bd ~ bd ~, ~ sd ~ sd, hh*4")Understanding these equivalencies lets you choose the style that fits your workflow. Functions are great for complex structures and variables; mini notation is concise for inline patterns.
Sequential Patterns: cat and fastcat
The simplest way to arrange patterns over time is with cat (short for โconcatenateโ).
cat / slowcat = < > angle brackets
cat plays each pattern for one complete cycle, then moves to the next.
Mini notation:
"<x y z>"=cat(x, y, z)
Using the function:
cat(
s("bd sd bd sd"),
s("hh hh hh hh"),
s("cp ~ cp ~")
)Same thing in mini notation:
s("<[bd sd bd sd] [hh hh hh hh] [cp ~ cp ~]>")Each pattern gets a full cycle. After all patterns play, it loops back to the beginning.
fastcat / seq = space between elements
fastcat (or its alias seq) squeezes all patterns into a single cycle.
Mini notation:
"x y z"=seq(x, y, z)=fastcat(x, y, z)
Using the function:
fastcat(
s("bd"),
s("sd"),
s("hh"),
s("cp")
)Same thing in mini notation (just spaces):
s("bd sd hh cp")All four sounds play within one cycle, each taking 1/4 of the time.
Comparing < > vs space
The difference becomes clear when you compare the same content:
< > angle brackets โ Each pattern takes one full cycle:
// cat = <>
note("<[c4 e4] [g4 b4]>").s("piano")space โ Both patterns fit in one cycle:
// seq = space
note("[c4 e4] [g4 b4]").s("piano")Use < > (cat) when you want patterns to take their time. Use spaces (seq) when building phrases within a single cycle.
Layering Patterns: stack = , comma
While cat arranges patterns in time, stack arranges them in spaceโplaying multiple patterns simultaneously.
Mini notation:
"x,y,z"=stack(x, y, z)
Basic Layering
Using the function:
stack(
s("bd ~ bd ~"),
s("~ hh ~ hh"),
s("~ ~ cp ~")
).room(.3)Same thing in mini notation (commas):
s("bd ~ bd ~, ~ hh ~ hh, ~ ~ cp ~").room(.3)All three patterns play at once, creating a basic drum beat.
Building Full Beats
Stack is perfect for layering drums, bass, and melody:
stack(
s("bd ~ bd ~"),
s("~ sd ~ sd"),
s("hh*8"),
note("<c2 g2>").s("sawtooth").lpf(400)
).room(.3)Each layer maintains its own pattern while playing together.
Combining Symbols: Nesting < >, ,, and spaces
You can nest these symbols freely. Use cat inside stack to create evolving layers:
Using functions:
stack(
s("bd ~ bd ~"),
cat(
s("~ sd ~ sd"),
s("~ sd ~ [sd sd]")
),
s("hh*8")
).room(.3)Same thing in pure mini notation:
s("bd ~ bd ~, <[~ sd ~ sd] [~ sd ~ [sd sd]]>, hh*8").room(.3)The snare pattern (inside < >) alternates each cycle while other layers (separated by ,) stay constant.
Hereโs how the symbols nest:
,separates simultaneous layers (stack)< >creates one-per-cycle alternation within a layer (cat)spacecreates subdivisions within each element (seq)
Rhythmic Structure: struct
While stack layers complete patterns, struct applies a rhythmic template to any pattern. Itโs one of Strudelโs most powerful tools for creating rhythmic variations.
What is struct?
struct takes a boolean pattern (using x for on and ~ for off) and applies it as a rhythmic mask to another pattern:
note("c3 e3 g3 b3")
.struct("x ~ x ~")
.s("piano")The notes c3 e3 g3 b3 are filtered through the rhythm x ~ x ~, so only notes on beats 1 and 3 play.
struct vs Other Approaches
There are multiple ways to create rhythmic patterns in Strudel. Hereโs when to use each:
Direct mini-notation โ When you know exactly what plays when:
note("c3 ~ e3 ~").s("piano")struct โ When you want to apply a rhythm to an existing pattern:
note("c3 e3 g3 b3")
.struct("x ~ x ~")
.s("piano")Both produce the same result, but struct separates what (the notes) from when (the rhythm).
The Equivalence Table
| What you want | Using struct | Direct equivalent |
|---|---|---|
| Play on beats 1 & 3 | .struct("x ~ x ~") | "c3 ~ e3 ~" |
| Every other beat | .struct("x ~") | "c3 ~" (repeats) |
| Syncopated | .struct("~ x ~ x") | "~ c3 ~ e3" |
| Euclidean 3 of 8 | .struct("x ~ ~ x ~ ~ x ~") | .euclid(3,8) |
| Complex rhythm | .struct("x ~ [x x] ~") | "c3 ~ [e3 g3] ~" |
Why Use struct?
1. Separate melody from rhythm:
// Define notes and rhythm separately
let melody = note("c4 d4 e4 f4 g4 a4 b4 c5")
let rhythm = "x ~ x ~ x ~ x x"
melody.struct(rhythm).s("piano")Now you can change the rhythm without touching the melody:
let melody = note("c4 d4 e4 f4 g4 a4 b4 c5")
let rhythm = "x x ~ x ~ x x ~" // different rhythm
melody.struct(rhythm).s("piano")2. Apply the same rhythm to multiple parts:
let groove = "x ~ [x ~] x ~ x [~ x] ~"
stack(
note("c2 c2 c2 c2 g1 g1 a1 b1").struct(groove).s("sawtooth").lpf(400),
note("c4 e4 g4 c5 e4 g4 c5 e5").struct(groove).s("piano").gain(0.6)
).room(0.3)Both bass and piano share the same rhythmic feel.
3. Create variations by changing only the structure:
let bassNotes = note("c2 eb2 g2 bb2")
cat(
bassNotes.struct("x x x x"), // straight
bassNotes.struct("x ~ x ~"), // half time
bassNotes.struct("x ~ x [~ x]"), // syncopated
bassNotes.struct("x x ~ x") // displaced
).s("sawtooth").lpf(600)Same notes, four different rhythmic feels.
struct with Samples
struct works with any pattern, including samples:
s("bd sd hh cp")
.struct("x ~ x ~ x ~ x x")The samples cycle through in order, but only play when the struct pattern has x.
Genre connection: This is how many house and techno producers thinkโa kit of sounds rotating through a rhythmic grid.
struct vs euclid
Both create rhythmic patterns, but work differently:
euclid โ Generates mathematically even distributions:
note("c3 e3 g3 b3")
.euclid(3, 8)
.s("piano")struct โ You define the exact rhythm:
note("c3 e3 g3 b3")
.struct("x ~ ~ x ~ ~ x ~")
.s("piano")Use euclid when you want mathematically distributed rhythms. Use struct when you have a specific rhythm in mind.
struct with Alternating Patterns
The structure pattern can alternate using < >:
note("c3 e3 g3 b3")
.struct("<[x x x x] [x ~ x ~] [x ~ ~ x]>")
.s("piano")Each cycle uses a different rhythm from the pattern.
struct for Drum Programming
struct is particularly powerful for drumsโdefine your kit, then program different patterns:
let kit = s("bd sd hh cp")
cat(
kit.struct("x ~ ~ ~ x ~ ~ ~"), // sparse intro
kit.struct("x ~ x ~ x ~ x ~"), // building
kit.struct("x x x x x x x x"), // full
kit.struct("x ~ x ~ ~ ~ x ~") // breakdown
).bank("RolandTR909")Think of struct as a step sequencer โ your sounds are loaded in slots, and the struct pattern determines which steps trigger.
Combining struct with Other Functions
struct + fast/slow:
note("c3 e3 g3 b3")
.struct("x ~ x ~")
.fast(2)
.s("piano")struct + stack:
stack(
s("bd").struct("x ~ x ~"),
s("sd").struct("~ x ~ x"),
s("hh").struct("x x x x")
).bank("RolandTR909")struct + cat:
note("c3 e3 g3 b3")
.struct(cat("x x x x", "x ~ x ~", "x ~ ~ x"))
.s("piano")Timed Arrangements: timeCat and arrange
Sometimes you need more control over how long each section plays.
timeCat / stepcat = @ for duration
timeCat lets you specify relative durations for each pattern.
Mini notation:
"x@3 y"= element x takes 3 steps, y takes 1 step
timeCat(
[3, s("bd sd bd")],
[1, s("cp")]
).room(.3)The first pattern takes 3/4 of the cycle, the second takes 1/4.
In mini notation, use @ with brackets for sub-patterns:
s("[bd sd bd]@3 cp").room(.3)For single elements, @ is simpler:
s("bd@3 cp")This holds โbdโ for 3 steps before โcpโ plays.
Useful for uneven phrase lengths:
timeCat(
[7, note("c4 d4 e4 f4 g4 a4 b4")],
[1, note("c5")]
).s("piano")arrange โ Multi-Cycle Song Structures
arrange is the most powerful tool for building complete songs. It lets you specify how many cycles each section should play:
arrange(
[4, s("hh*8").room(.2)],
[4, stack(s("bd ~ bd ~"), s("hh*8")).room(.2)],
[2, s("cp*4").room(.5)]
)- First section: 4 cycles of hi-hats only
- Second section: 4 cycles of kick + hi-hats
- Third section: 2 cycles of claps
The pattern loops after all sections complete (10 cycles total).
Advanced Layering: polymeter = { } curly braces
Polyrhythmic Patterns
polymeter creates layers that cycle at different rates, creating complex polyrhythms.
Mini notation:
"{a b c, x y}"=polymeter([a,b,c], [x,y])
Using the function:
polymeter(
["c4", "e4", "g4"],
["c5", "d5"]
).note().s("piano").room(.4)Same thing in mini notation (curly braces):
note("{c4 e4 g4, c5 d5}").s("piano").room(.4)The first layer has 3 notes, the second has 2. They cycle independently, creating shifting relationships. The curly braces { } tell Strudel to keep each layerโs step count independent.
Genre-Specific Arranging Techniques
Different genres have characteristic arrangement patterns. Hereโs how to create them in Strudel.
Pop/Rock: Verse-Chorus Structure
Pop songs reuse sections. Define your parts as variables, then arrange them:
let verse = stack(
s("bd ~ bd ~"),
s("~ sd ~ sd"),
note("<c3 g3>").s("sawtooth").lpf(600)
)
let chorus = stack(
s("bd sd bd sd"),
s("hh*8"),
note("<c3 e3 g3 c4>").s("sawtooth").lpf(1200)
)
arrange(
[4, verse],
[4, chorus],
[4, verse],
[4, chorus]
).room(.3).cpm(70)The verse is sparse, the chorus is fuller. This contrast creates the typical pop dynamic.
Adding a bridge:
let verse = stack(
s("bd ~ bd ~"),
s("~ sd ~ sd"),
note("<c3 g3>").s("sawtooth").lpf(600)
)
let chorus = stack(
s("bd sd bd sd"),
s("hh*8"),
note("<c3 e3 g3 c4>").s("sawtooth").lpf(1200)
)
let bridge = stack(
s("~ ~ bd ~"),
note("<a2 f2>").s("sawtooth").lpf(400)
)
arrange(
[4, verse],
[4, chorus],
[4, verse],
[4, chorus],
[4, bridge],
[4, chorus]
).room(.3).cpm(70)EDM: Loop-Based with Buildups
EDM arranges loops with gradual additions and filter sweeps:
let intro = s("hh*8").lpf(800)
let buildup = stack(
s("hh*16"),
s("bd").fast("<1 2 4 8>")
).lpf(sine.range(400, 8000).slow(4))
let drop = stack(
s("bd ~ [~ bd] ~"),
s("~ cp ~ cp"),
s("hh*8"),
note("c2").s("sawtooth").lpf(200)
)
arrange(
[4, intro],
[4, buildup],
[8, drop]
).room(.2).cpm(70)The buildup uses a filter sweep (lpf(sine.range(...))) to create tension before the drop.
Adding a breakdown:
let drop = stack(
s("bd ~ [~ bd] ~"),
s("~ cp ~ cp"),
s("hh*8"),
note("c2").s("sawtooth").lpf(200)
)
let breakdown = s("~ ~ ~ ~").room(.5)
let buildup = stack(
s("hh*16"),
s("bd").fast("<1 2 4 8>")
).lpf(sine.range(400, 8000).slow(4))
arrange(
[8, drop],
[2, breakdown],
[4, buildup],
[8, drop]
).room(.2).cpm(70)Classical: Theme and Variation
Classical music develops themes through transformation. Use cat to present variations:
let theme = note("c4 d4 e4 g4")
cat(
theme,
theme.rev(),
theme.add(note(5)),
theme.fast(2)
).s("piano").room(.4)Each cycle presents:
- Original theme
- Retrograde (reversed)
- Transposed up a fourth
- Diminution (twice as fast)
More elaborate variations:
let theme = note("c4 d4 e4 g4")
cat(
theme,
theme.rev(),
theme.add(note(7)),
stack(theme, theme.add(note(4))),
theme.slow(2),
theme.add(note("<0 12>"))
).s("piano").room(.4).cpm(40)This adds:
- Harmonized (thirds added)
- Augmentation (twice as slow)
- Octave displacement
Bach Prelude in C Major (BWV 846):
Bachโs famous prelude demonstrates how to build complex pieces from simple arpeggiated patterns. The entire piece uses the same rhythmic figureโan 8-note arpeggio patternโapplied to different chord voicings:
// Bach - Prelude in C Major BWV 846 (first 10 measures)
cat(
"[c4,e4,g4,c5,e5]!2",
"[c4,d4,a4,d5,f5]!2",
"[b3,d4,g4,d5,f5]!2",
"[c4,e4,g4,c5,e5]!2",
"[c4,e4,a4,e5,a5]!2",
"[c4,d4,fs4,a4,d5]!2",
"[b3,d4,g4,d5,g5]!2",
"[b3,c4,e4,g4,c5]!2",
"[a3,c4,e4,g4,c5]!2",
"[d3,a3,d4,fs4,c5]!2"
).note()
.arp("0 1 2 3 4 2 3 4")
.s("piano")
.sustain(0.5)
.cpm(70)Key techniques used:
- Chord voicings as stacked notes โ
[c4,e4,g4,c5,e5]defines the exact notes of each chord !2repetition โ Each chord plays twice (one full measure in the original)cat()for sequencing โ Chords flow one after another.arp()pattern โ The signature"0 1 2 3 4 2 3 4"arpeggio figure (root, 2nd, 3rd, 4th, 5th, 3rd, 4th, 5th)
This approach separates harmony (the chord voicings) from rhythm (the arpeggio pattern), making it easy to modify either independently.
Hip-Hop: Sample Loops with Layers
Hip-hop builds on a core loop, adding and removing elements for sections:
let drums = stack(
s("bd ~ bd ~"),
s("~ sd ~ sd")
)
let hats = s("hh*8").gain(.6)
let bass = note("<c2 c2 g1 g1>").s("sawtooth").lpf(300)
arrange(
[4, drums],
[4, stack(drums, bass)],
[8, stack(drums, bass, hats)],
[4, stack(drums, bass)]
).room(.2).cpm(90)Elements are introduced gradually:
- Drums only (intro)
- Drums + bass (verse starts)
- Full beat with hats (chorus)
- Drop the hats (back to verse)
Ambient: Evolving Textures
Ambient music uses slow evolution and layered drones:
stack(
note("c3").s("sawtooth").lpf(sine.range(200, 800).slow(16)),
note("g3").s("sine").gain(.5).slow(2),
cat(
note("c5 e5 g5 ~"),
note("d5 f5 a5 ~"),
note("e5 g5 b5 ~"),
note("c5 e5 g5 ~")
).s("sine").gain(.3).slow(4)
).room(.8).delay(.5).cpm(20)Long cycles with subtle changes create an evolving soundscape.
Combining Techniques
Real arrangements often nest these functions. Hereโs a more complete example:
let kick = s("bd ~ [~ bd] ~")
let snare = s("~ sd ~ sd")
let hats = s("hh*8").gain(.7)
let verse_drums = stack(kick, snare)
let chorus_drums = stack(kick, snare, hats)
let verse_bass = note("<c2 c2 g1 a1>").s("sawtooth").lpf(400)
let chorus_bass = note("<c2 e2 g2 c3>").s("sawtooth").lpf(800)
let verse = stack(verse_drums, verse_bass)
let chorus = stack(chorus_drums, chorus_bass)
arrange(
[4, verse_drums],
[8, verse],
[8, chorus],
[8, verse],
[8, chorus],
[4, chorus_drums]
).room(.3).cpm(85)This creates a complete song structure:
- Drums intro (4 cycles)
- Verse with bass (8 cycles)
- Chorus with fuller arrangement (8 cycles)
- Repeat verse and chorus
- Drums outro (4 cycles)
Quick Reference
| Function | Mini Notation | Description |
|---|---|---|
cat(x,y) | "<x y>" | One pattern per cycle, sequential |
seq(x,y) / fastcat | "x y" | All patterns in one cycle |
stack(x,y) | "x,y" | Patterns play simultaneously |
.struct("x ~ x ~") | โ | Apply rhythmic template to pattern |
timeCat([3,x],[1,y]) | "x@3 y" | Proportional timing within cycle |
arrange([4,x],[8,y]) | โ | Multi-cycle song sections |
polymeter([a,b,c],[x,y]) | "{a b c, x y}" | Polyrhythmic layering |
.euclid(3,8) | "x(3,8)" | Euclidean rhythm distribution |
Tips for Effective Arranging
- Start simple โ Begin with a basic loop, then add complexity
- Use variables โ Name your sections for easy reuse
- Create contrast โ Sections should sound different (sparse vs. full, low vs. high energy)
- Think in powers of 2 โ 4, 8, 16 cycle sections feel natural
- Leave space โ Silence and breakdowns make the full sections hit harder
- Layer strategically โ Each layer should have its own frequency range
Try combining these techniques to build your own complete tracks!