Movement

EDM.5: transforms, probability, and making patterns breathe

Play your L4a capstone twice. You hear the seam. Every cycle is identical. Real music shifts. Strudel has tools for that.

about this lesson

Repertoire: Lost Woods / Saria’s Song (Koji Kondo, Zelda: OoT, 1998) — the melody that changes meaning without changing notes. Megalovania (Toby Fox, Undertale, 2015) — rhythmic persistence with everything shifting around it. Fez (Disasterpeace, 2012) — patterns that evolve over minutes through subtle parameter shifts.

Parallel composition: Your ambient piece starts moving. The koto pattern transforms. The piece breathes.

What you already know: Everything from L0-L4b. Patterns, drums, bass, layers, space, chords.

00Conditional Repeats

The hi-hat pattern from EDM.4. Steady, unchanging:

steady hats

Same pattern, but every 4th cycle the accents reverse:

hats + .every(4, rev)

Three cycles forward. One cycle reversed. The accent pattern flips. Your ear catches the difference, then the regularity returns.

.every()

.every(N, transform): apply a transform every Nth cycle. The rest of the time, the pattern plays normally. Predictable variation—surprise you can count on.

Stacking transforms

Different elements, different periods. The kick doubles every 4th cycle. The cowbell reverses every 3rd:

layered .every()

The kick doubles every 4 cycles. The cowbell reverses every 3. They realign every 12 cycles. Different periods create a longer arc—the pattern takes 12 cycles before it truly repeats.

tweak it

Replace fast(2) with ply(2)—repeats each hit instead of speeding the whole pattern. Try .every(8, rev) for less frequent variation. Or .every(2, fast(2)) on the cowbell for something aggressive.

01Probability

.every() is a clock. .sometimes() is a coin flip:

sometimes fast hats

Each cycle, roughly 50/50 whether the hats double. You can’t predict when. The pattern breathes differently every time.

.sometimes() / .often() / .rarely()

.sometimes(transform): ~50% chance per cycle. .often(): ~75%. .rarely(): ~25%. The pattern makes choices. Non-deterministic. It sounds live because it is.

Probability across layers

Different elements, different odds. The clap sometimes drenches in reverb, the hats rarely double, the cowbell often echoes:

probability stack

The clap sometimes gets drenched, the hats rarely double, and the cowbell often echoes. Each element has its own odds. The beat drifts instead of looping.

tweak it

Change .rarely(fast(2)) to .often(fast(2))—the hats get manic. Try .sometimes(rev) on the kick for unexpected rhythm flips. Chain both: .every(4, fast(2)).sometimes(rev) on a single pattern.

rabbit hole: determinism and live coding

Why randomness matters

A looping pattern is a machine. A pattern with probability is a system. Same difference as a metronome vs. a drummer. The drummer makes micro-decisions on every hit. .sometimes() is the cheapest way to get that quality. No two listens are the same.

Algorave

Live coding communities (Algorave, TOPLAP) build entire performances from transforms like these. The performer writes code in real time. The audience watches the screen and dances. Strudel and TidalCycles are the two main instruments. .every() and .sometimes() are standard vocabulary: set up a system, then nudge it, rather than programming every note.

Mixing .every and .sometimes

combined transforms

The kick doubles on a schedule AND rarely reverses. The hats reverse on a schedule AND sometimes double. Layered rules produce patterns no single rule could.

Steve Reich

In 1965, Steve Reich made “It’s Gonna Rain” by playing two identical tape loops on slightly different machines. They started in sync and slowly drifted apart. Phasing. .every() with different periods on different layers does the same thing—patterns that share a downbeat but disagree on when to change. Reich called it “the process.” Strudel calls it a one-liner.

02Stereo Splits

.pan() from EDM.3 places a sound left or right. .jux() splits the pattern in two: original in one ear, a transformed copy in the other.

jux: L=forward, R=reversed

Left ear: forward accents. Right ear: reversed accents. Same hits, mirrored in stereo. The hat rolls weave from side to side. Use headphones.

.jux()

.jux(transform): play the original pattern on the left, a transformed copy on the right. Short for “juxtapose.” .juxBy(amount, transform) controls how far apart: 0.5 means half-panned instead of hard L/R.

tweak it

Try .juxBy(0.3, rev) for a subtler split. Or .jux(fast(2))—normal speed left, double speed right. Apply it to the cowbell: s("~ cb ~ cb ~ cb cb ~").gain(0.5).jux(rev).

Offset layering

.off() copies the pattern, delays it by a fraction of the cycle, and transforms the copy. Both play at once:

pad + octave echo

The pad plays its chord. Half a cycle later, a copy plays the same chord an octave higher (add(note(12)) = 12 semitones up). Two layers from one line. The pad thickens without writing a second pattern.

.off()

.off(time, transform): copy the pattern, delay it by time (fraction of a cycle), apply transform, stack both. .off(1/8, add(note(7))) creates a canon at the fifth, offset by an eighth note.

Offset as harmony

A fifth above (7 semitones) instead of an octave. Offset by an eighth note instead of half a bar:

pad + delayed fifth

The offset copy is a fifth higher and arrives a beat later. The chords cascade. .off() turned one pad into two voices.

tweak it

Try .off(1/4, x => x.add(note(12)).gain(0.15).pan(0.8))—the offset copy is an octave up, quieter, and panned right. Or stack offsets: .off(1/8, add(note(7))).off(1/4, add(note(12))) for two shadow copies.

03Hear Movement in the Wild

Three composers, three approaches to making patterns evolve.

Zelda: Lost Woods / Saria’s Song (Koji Kondo, 1998). The same 8-bar melody appears in five Zelda games. In OoT, it’s playful. In Twilight Princess, it’s haunted. In BotW, it’s barely there. Same notes. Different transforms. That’s .every() as game design:

lost woods (verify notes) + transforms

Remove the .every(4, rev) and .sometimes() lines. Play it again. Static. Add them back. Movement.

Undertale: Megalovania (Toby Fox, 2015). The melody barely changes. Everything around it does. The bass shifts, the rhythm stutters, the intensity builds. That’s .off() creating shadows:

megalovania (verify notes) + offset

Fez (Disasterpeace, 2012). Patterns that evolve so slowly you don’t notice until you’ve been listening for three minutes. That’s .sometimes() with low probability and .degradeBy() thinning events out:

fez-style glacial evolution

Let it run for 30 seconds. It’s never the same twice. The .degradeBy(0.3) randomly drops 30% of events. The .sometimes() shifts some notes up. The piece drifts.

rabbit hole: jo-ha-kyū and the shape of everything

Three parts

Jo (序): introduction. Slow, sparse, establishing. Ha (破): breaking apart. Acceleration, intensification, development. Kyū (急): rapid conclusion. Everything at once, then done.

Zeami Motokiyo codified this for Noh theater in the 1400s. But it shows up everywhere in Japanese art: tea ceremony (slow preparation → the pour → the drink), calligraphy (the approach → the stroke → the lift), martial arts (the stance → the exchange → the cut).

In music

Gagaku (Japanese court music) uses jo-ha-kyū as formal structure. The piece begins slowly, accelerates through a middle section, and arrives at a rapid conclusion. The tempo literally increases.

In your Strudel code: .fast() IS kyū. .slow() IS jo. The transforms you just learned (.every(), .sometimes()) create the ha: the middle section where things start breaking apart and recombining.

In your composition

Your ambient piece has been in jo since L0. Sparse, slow, establishing. L5 is where it enters ha: the patterns start transforming, the piece starts moving. L6 will bring kyū: the full arrangement, the build, the peak.

rabbit hole: the Ma Engine — silence as probability

Algorithmic breathing

Traditional Japanese music breathes. Notes happen, then silence, then notes. The spacing isn’t metronomic. It fluctuates. You can model this in Strudel with probability:

.degradeBy(0.3) randomly removes 30% of events. Each cycle, different events disappear. The pattern thins and thickens like breath.

.sometimes(x => x) applies a transform with ~50% probability. Combine it with .degradeBy() and your pattern is never the same twice, but it’s always recognizable. The motif survives. The details shift.

Density modulation

Want the piece to breathe slowly? Use .degradeBy(sine.range(0.1, 0.5).slow(8)). The sine wave oscillates the degradation amount over 8 cycles: dense, then sparse, then dense again. The piece inhales and exhales.

This is ma as code. The silence isn’t fixed. It’s alive.

04Your Composition

The koto pattern starts transforming. The shakuhachi drifts. The piece enters ha:

your ambient piece: movement

The koto reverses every 4 cycles. Sometimes a note shifts up a fourth. The shakuhachi randomly drops notes, breathing. The pad holds everything. Let it run. It’s never the same twice.

05Movement

The EDM.4 track, alive. Kicks that double, hats that split, a clap that drifts between dry and drenched, a cowbell that reverses, and a pad echoing itself an octave up. Same chords, same key. Transforms make it breathe.

// TITLE: movement
compose
  1. Add .sometimes(rev) to the 808 bass. The melody contour reverses at random—different shape, same notes.
  2. Apply .jux(x => x.fast(2).gain(0.3)) to the cowbell. Double-time ghost cowbell in the opposite ear.
  3. Chain transforms on the hats: .every(8, fast(2)).sometimes(rev).juxBy(0.5, rev). Three rules, one pattern.
  4. Share it. The track is different every time you play it. That’s the point.
what you earned
tooldoeslooks like
.every(N, fn)transform every Nth cycle.every(4, fast(2))
.sometimes(fn)~50% chance per cycle.sometimes(rev)
.often(fn)~75% chance per cycle.often(fast(2))
.rarely(fn)~25% chance per cycle.rarely(rev)
fast(N)speed up pattern by Nfast(2)
revreverse pattern orderrev
ply(N)repeat each event N timesply(2)
.jux(fn)L=original, R=transformed.jux(rev)
.juxBy(n, fn)partial stereo split.juxBy(0.5, rev)
.off(t, fn)offset copy + transform.off(1/8, add(note(7)))
add(note(N))transpose by N semitonesadd(note(12))
movement checklist

.every() for scheduled variation. .sometimes() for probability. .jux() for stereo splits. .off() for time-shifted harmony. Layer them. Different periods and probabilities on different elements. The complexity builds itself.

Next: The Drop. Arrangement, silence, tension and release.

listening

Tracks that demonstrate this lesson’s concepts.

artisttrackwhy
Koji KondoZelda OoT: Lost Woods (1998)(VGM) Mixolydian mode, the melody that changes meaning across 5 games
Toby FoxUndertale: Megalovania (2015)(VGM) rhythmic persistence, everything shifts except the melody
DisasterpeaceFez (2012)(game) glacial evolution, patterns shifting over minutes
DJ RashadFeelin (2012)(footwork) constant rhythmic instability, scattered samples
BurialArchangel (2007)(garage) constantly shifting chopped vocals, ghostly evolution
Aphex TwinXtal (1992)(ambient) subtle timbral shifts, generative ambient
Alex McLean (Yaxu)Algorave performance (2013)(footwork) TidalCycles live coding: .every() and .sometimes() as performance vocabulary
history

The idea that rules can generate music—not specific notes, but systems that produce notes—is older than computers.

Musikalisches Würfelspiel (1757–1790s)

“Musical dice games” attributed to various composers (sometimes falsely to Mozart) let players roll dice to select pre-composed bars, assembling a minuet from random choices. The system guaranteed grammatically correct music regardless of rolls. Each roll was a .sometimes()—probability selecting from a curated set.

Steve Reich and phasing (1965–1968)

Reich discovered phasing accidentally when two identical tape loops on different machines slowly drifted apart. “It’s Gonna Rain” (1965) and “Come Out” (1966) explored this. “Piano Phase” (1967) applied it to live performers. The principle: identical patterns at slightly different speeds generate emergent complexity. That’s what .every() with different periods on different layers does—patterns that share a start point but disagree on when to change.

Live coding and TOPLAP (2003–present)

In 2003, a group of artists including Alex McLean, Adrian Ward, and others drafted the TOPLAP manifesto at a Hamburg workshop. “Show us your screens” was the core demand—audiences should see the code being written. McLean went on to create TidalCycles (2009), the Haskell-based live coding language that Strudel ports to JavaScript. The Algorave movement (algorithmic rave) emerged in 2012, with performances in clubs from Sheffield to Tokyo.

Sources: Hedges, “Dice Music in the Eighteenth Century” (1978); Reich, Writings on Music (2002); Collins et al., “Live Coding in Laptop Performance” (2003); McLean, “Making Programming Languages to Dance to” (2014).

→ explore the full timeline