Skip to content

Arrangements

Timelines let you arrange patterns along a fixed-length structure. Unlike normal pattern assignment (which loops indefinitely), a timeline has a defined beginning and end with patterns placed at explicit cycle positions.

use "std/instruments" { Sampler, Kit };
let drums = AudioTrack("drums");
drums.load_instrument(Sampler(Kit("cr78")));
let song = timeline(16);
song.at(0, 8) << [bd _ sd _]; // Verse: cycles 0-7
song.at(8, 8) << [bd*4]; // Chorus: cycles 8-15
drums << song;
PLAY;

timeline(length) creates a timeline of the specified length in cycles.

let t = timeline(16); // 16-cycle timeline
let short = timeline(4); // 4-cycle timeline

The length must be greater than 0.

Use .at(start, duration) to create a slot, then << to assign a pattern to it.

let t = timeline(8);
t.at(0, 4) << [bd _ sd _]; // Place pattern at cycle 0, lasting 4 cycles
t.at(4, 4) << [bd bd sd bd]; // Place pattern at cycle 4, lasting 4 cycles
ParameterTypeDescription
startNumberStart cycle offset (must be >= 0)
durationNumberDuration in cycles (must be > 0)

Any pattern can be placed in a slot — mini-notation, euclidean, script patterns, or any combinator:

t.at(0, 4) << [bd sd]; // Mini-notation
t.at(4, 4) << euclid(3, 8); // Euclidean rhythm
t.at(8, 4) << [C4 D4 E4 F4]; // Note pattern

Assign the completed timeline to an audio track with <<:

let drums = AudioTrack("drums");
drums.load_instrument(Sampler(Kit("cr78")));
let t = timeline(8);
t.at(0, 4) << [bd _ sd _];
t.at(4, 4) << [bd*4];
drums << t;

The child pattern is queried cycle-by-cycle across the slot’s duration. Single-cycle patterns like mini-notation repeat every cycle; multi-cycle patterns like cat(...) progress through their cycles and wrap naturally when the slot outlasts the pattern.

let t = timeline(16);
// Single-cycle pattern: plays the same figure each cycle
t.at(0, 8) << [bd sd hh cp];
// Multi-cycle pattern (2 cycles): A B A B A B A B across the 8-cycle slot
t.at(8, 8) << cat([bd sd], [hh cp]);

Cycles with no entry produce silence. Use this for breaks and pauses.

let t = timeline(16);
t.at(0, 4) << [bd sd bd sd]; // Cycles 0-3: drums
// Cycles 4-7: silence
t.at(8, 4) << [bd _ _ sd]; // Cycles 8-11: drums
// Cycles 12-15: silence

When entries overlap, last-start-wins: the entry with the higher start cycle takes priority in the overlapping region.

let t = timeline(8);
t.at(0, 8) << [bd*4]; // Background for all 8 cycles
t.at(4, 4) << [cp cp cp cp]; // Override cycles 4-7
// Result: cycles 0-3 play [bd*4], cycles 4-7 play [cp cp cp cp]

If two entries start at the same cycle, the one added last wins.

Use separate timelines for each track to build a full song structure.

use "std/instruments" { Sampler, Kit };
let len = 16;
// Kicks
let kick_tl = timeline(len);
kick_tl.at(0, 8) << [bd _ bd _]; // Verse
kick_tl.at(8, 8) << [bd*4]; // Chorus
let kicks = AudioTrack("kicks");
kicks.load_instrument(Sampler(Kit("cr78")));
kicks << kick_tl;
// Snares
let snare_tl = timeline(len);
snare_tl.at(0, 8) << [_ _ sd _]; // Verse
snare_tl.at(8, 8) << [_ sd _ sd]; // Chorus
let snares = AudioTrack("snares");
snares.load_instrument(Sampler(Kit("cr78")));
snares << snare_tl;
// Hi-hats (throughout)
let hat_tl = timeline(len);
hat_tl.at(0, 16) << [_ hh];
let hats = AudioTrack("hats");
hats.load_instrument(Sampler(Kit("cr78")));
hats << hat_tl;
PLAY;
MethodReturnsDescription
.at(start, duration)TimelineSlotCreate a slot (accepts pattern via <<)
.length()NumberTimeline length in cycles
.entries()NumberNumber of pattern entries
let t = timeline(16);
t.length(); // 16
t.entries(); // 0
t.at(0, 8) << [bd sd];
t.entries(); // 1