Skip to content

Pattern Methods

Pattern methods transform or inspect a pattern value and chain freely: [C4 D4 E4].fast(2).transpose(5).reverse().

MethodReturnsMeaning
fast(factor)PatternSpeed up by factor
slow(factor)PatternSlow down by factor
transpose(semitones)PatternShift pitches in semitones
reverse()PatternMirror events within each cycle
rotate(steps)PatternRotate events by steps, wrapping
stack(other)PatternLayer two patterns simultaneously
cat(other) / concat(other)PatternAlternate patterns cycle by cycle
euclid(hits, steps [, rotation])PatternEuclidean rhythm
degrade(probability)PatternRandomly drop events
nudge(offsets)PatternPer-step timing offsets
swing(amount)PatternShift odd-indexed events forward
humanize(amount)PatternPseudo-random timing variation
in_key(key)PatternResolve scale degrees against a key
quantize(key)PatternSnap pitches to the nearest scale tone
map(fn)PatternTransform each note through a function
map_with_timing(fn)PatternTransform with position and duration
iter()IteratorInfinite iterator over events
at(index)PatternElement at index (same as p[index])
length()NumberEvent count in cycle 0
describe()StringHuman-readable structure description
kind()StringConcrete pattern kind
elements()ArrayChild sub-patterns

Speed up the pattern — fast(2) fits two cycles into one.

let doubled = [C4 D4 E4 F4].fast(2);
let varied = [C4 D4].fast(<1 2 4>); // patterned factor, changes per cycle

Slow down the pattern — slow(2) stretches one cycle over two.

let halftime = [C4 D4 E4 F4].slow(2);

Shift all pitches by semitones (negative shifts down). On sample patterns this applies a pitch-shift instead.

let octave_up = [C4 D4 E4].transpose(12);
let down_fifth = [C4 D4 E4].transpose(-7);
let moving = [C4 E4 G4].transpose([0 7 12]); // per-cycle shift

Mirror events within each cycle.

let back = [C4 D4 E4 F4].reverse(); // F4 E4 D4 C4

Rotate events within each cycle, wrapping around. Positive rotates right ([C4 D4 E4].rotate(1)[E4 C4 D4]), negative rotates left.

let right = [C4 D4 E4].rotate(1);
let left = [C4 D4 E4].rotate(-1); // [D4 E4 C4]

Layer two patterns so they play simultaneously.

let layered = [C4 E4 G4].stack([C3 G3]);

Alternate two patterns cycle by cycle: the receiver plays on even cycles, the argument on odd cycles. concat is an alias for cat.

let ab = [C4 D4 E4].cat([F4 G4]);

Distribute hits onsets as evenly as possible over steps slots (Bjorklund algorithm). The optional third argument rotates the result by that many steps.

let tresillo = [C4].euclid(3, 8);
let rotated = [C4].euclid(3, 8, 2);

Randomly drop events with the given probability (0–1). Deterministic per event time, so a pattern degrades the same way on every run.

let sparse = [C4 D4 E4 F4].degrade(0.3); // each event has a 30% chance to drop
let rising = [C4 D4].degrade([0.0 0.5 0.9]); // per-cycle probability

All microtiming amounts are step-relative fractions: 0.25 means a quarter of the event’s own step size. Durations are preserved.

Shift events by per-step offsets. Accepts a single number (applied to every event), an array #[...] of offsets cycled through per event, or a pattern (queried per cycle).

let pushed = [C4 D4 E4 F4].nudge(0.1);
let groove = [C4 D4 E4 F4].nudge(#[0, 0.25, 0, -0.1]);
let drift = [C4 D4].nudge(<0.0 0.1>);

Shift odd-indexed events forward by amount; even-indexed events stay on the grid.

let swung = [C4 D4 E4 F4].swing(0.25);

Add a pseudo-random offset in [-amount, +amount] to each event. Offsets are derived by hashing the event time, so results are deterministic and reproducible.

let loose = [C4 D4 E4 F4].humanize(0.05);

Resolve scale-degree elements (^1, ^5:maj7, …) to concrete pitches using a key. MIDI notes and samples pass through unchanged.

let k = Key(C4, "major");
let melody = [^1 ^3 ^5 ^-2].in_key(k);
let diatonic = [^1: ^4: ^5:].in_key(Key(A3, "minor"));

Snap MIDI pitches to the nearest scale tone of the key. Degrees and samples are left unchanged.

let snapped = [C4 C#4 D4 D#4].quantize(Key(C4, "major"));

Transform each event through a function. The function receives the MIDI pitch as a number and returns the replacement — a note, number, array, pattern, or rest.

let up = [C4 D4 E4].map(fn(note) { return note + 12; });
let split = [C4 G4].map(fn(note) {
if note > 64 { return note + 12; }
return note;
});

Like map, but the function also receives start (cycle position, 0–1) and dur (event duration in cycles).

let accented = [C4 D4 E4 F4].map_with_timing(fn(note, start, dur) {
if start == 0.0 { return note + 12; } // lift the downbeat
return note;
});

Create an infinite iterator over the pattern’s events. Each item is an event value carrying note, start, and duration. Always bound it with .take(n).

let events = [C4 D4 E4].iter().take(6).collect();
for event in [C4 E4 G4].iter().take(3) {
PRINT event.note();
}

Element at a zero-based index, returned as a pattern. p[index] is equivalent. Works on indexable patterns (sequences, stacks, alternations); out-of-range indices are an error.

let seq = [C4 D4 E4 F4];
let first = seq.at(0);
let third = seq[2];

Number of events in cycle 0.

let n = [C4 D4 E4].length(); // 3

Human-readable description of the pattern’s structure — useful for debugging chained transforms.

let what = [C4 D4].fast(2).reverse().describe();
PRINT what;

The concrete pattern kind as a string ("Sequence", "Stack", "Fast", …).

let k = [C4 D4].fast(2).kind(); // "Fast"

Child sub-patterns as an array — the layers of a stack, the parts of a concatenation, and so on.

let layers = [C4 D4].stack([E4 F4]).elements();
PRINT layers.length(); // 2