Arrangements
So far every pattern you’ve sent to a track loops forever. That’s perfect for live coding, but a finished piece usually has a shape: an intro, a verse, a chorus, a breakdown. A timeline gives you that shape. Think of it like the arrangement view in a DAW — a fixed-length ruler where you drop patterns at specific positions, and silence wherever you don’t.
Here’s a two-section drum arrangement from start to finish:
use "std/instruments" { Sampler, Kit };
let drums = AudioTrack("drums");drums.load_instrument(Sampler(Kit("CR-78")));
let song = timeline(16);song.at(0, 8) << [bd _ sd _]; // verse: cycles 0–7song.at(8, 8) << [bd*4]; // chorus: cycles 8–15
drums << song;PLAY;You build a 16-cycle timeline, place a sparse beat in the first half and a
four-on-the-floor in the second, then send the whole thing to a track with <<
just like any other pattern. Let’s take it apart.
Creating a Timeline
Section titled “Creating a Timeline”timeline(length) makes an empty timeline that’s length cycles long. The
length is the total runtime of the arrangement, in cycles.
let t = timeline(16); // 16 cycles longlet short = timeline(4); // 4 cycles longThe length must be greater than zero.
Placing Patterns
Section titled “Placing Patterns”A timeline is empty until you put something in it. Carve out a slot with
.at(start, duration), then assign a pattern to that slot with <<:
let t = timeline(8);t.at(0, 4) << [bd _ sd _]; // starts at cycle 0, lasts 4 cyclest.at(4, 4) << [bd bd sd bd]; // starts at cycle 4, lasts 4 cyclesstart is the cycle the slot begins on (zero or greater); duration is how many
cycles it lasts (greater than zero). Anything that’s a pattern can go in a slot —
mini-notation, a euclidean rhythm, a note sequence, or any combinator:
let t = timeline(12);t.at(0, 4) << [bd sd]; // mini-notationt.at(4, 4) << euclid(3, 8, [bd]); // euclidean rhythmt.at(8, 4) << [c4 e4 g4 c5]; // a melodyAssigning to a Track
Section titled “Assigning to a Track”A timeline isn’t sound on its own — it’s a pattern source. Send it to a track
with <<, exactly as you would a plain pattern:
use "std/instruments" { Sampler, Kit };
let drums = AudioTrack("drums");drums.load_instrument(Sampler(Kit("CR-78")));
let t = timeline(8);t.at(0, 4) << [bd _ sd _];t.at(4, 4) << [bd*4];
drums << t;PLAY;When the timeline reaches its end it loops back to the start, so an arrangement repeats just like any other pattern would.
Multi-Cycle Entries
Section titled “Multi-Cycle Entries”A slot queries its pattern one cycle at a time across the slot’s whole duration.
A single-cycle pattern — most mini-notation — simply repeats every cycle. A
multi-cycle pattern like one built with .cat() advances through its own cycles
and wraps around naturally when the slot outlasts it:
let t = timeline(16);
// single-cycle: the same figure plays every cycle of the slott.at(0, 8) << [bd sd hh cp];
// two-cycle pattern over an 8-cycle slot: A B A B A B A Bt.at(8, 8) << [bd sd].cat([hh cp]);Any cycle with no slot covering it is silent. Gaps aren’t an oversight — they’re how you write breaks, drops, and breathing room into a piece:
let t = timeline(16);t.at(0, 4) << [bd sd bd sd]; // cycles 0–3: drums // cycles 4–7: silencet.at(8, 4) << [bd _ _ sd]; // cycles 8–11: drums // cycles 12–15: silenceOverlapping Entries
Section titled “Overlapping Entries”When two slots cover the same cycle, last start wins: the slot with the higher start cycle takes over in the overlap. This makes it easy to lay down a background figure and then punch a variation over part of it.
let t = timeline(8);t.at(0, 8) << [bd*4]; // background for all 8 cyclest.at(4, 4) << [cp cp cp cp]; // overrides cycles 4–7
// cycles 0–3 play [bd*4], cycles 4–7 play [cp cp cp cp]If two slots start on the same cycle, the one you added last wins.
A Full Song
Section titled “A Full Song”Real arrangements use one timeline per track, all the same length, so the sections line up. Here a kick, snare, and hi-hat play a sparse verse for eight cycles and then open up into a busier chorus:
use "std/instruments" { Sampler, Kit };
let len = 16;
let kick_tl = timeline(len);kick_tl.at(0, 8) << [bd _ bd _]; // versekick_tl.at(8, 8) << [bd*4]; // choruslet kicks = AudioTrack("kicks");kicks.load_instrument(Sampler(Kit("CR-78")));kicks << kick_tl;
let snare_tl = timeline(len);snare_tl.at(0, 8) << [_ _ sd _]; // versesnare_tl.at(8, 8) << [_ sd _ sd]; // choruslet snares = AudioTrack("snares");snares.load_instrument(Sampler(Kit("CR-78")));snares << snare_tl;
let hat_tl = timeline(len);hat_tl.at(0, 16) << [_ hh]; // throughoutlet hats = AudioTrack("hats");hats.load_instrument(Sampler(Kit("CR-78")));hats << hat_tl;
PLAY;Inspecting a Timeline
Section titled “Inspecting a Timeline”Two accessors let you check what you’ve built. .length() returns the timeline’s
length in cycles, and .entries() returns how many slots you’ve filled:
let t = timeline(16);show(t.length()); // 16show(t.entries()); // 0
t.at(0, 8) << [bd sd];show(t.entries()); // 1Next Steps
Section titled “Next Steps”With an arrangement in place, render it to an audio file.
- Rendering — bounce tracks and stems to
.wav