Skip to content

Transform a Pattern

Each recipe starts from a pattern you already have and returns a new one. The original is never changed, so you can derive as many variations from one motif as you need. Every method is listed in the pattern methods reference.

.fast(n) plays the pattern n times per cycle; .slow(n) stretches it across n cycles.

use "std/instruments" { SamplerMelodic, Kit };
let lead = AudioTrack("lead");
lead.set_instrument(SamplerMelodic(Kit("keys")));
lead << [C4 E4 G4 C5].reverse().fast(2); // descending, twice per cycle
PLAY;

[C4 D4 E4].fast(2) and the mini-notation [[C4 D4 E4]*2] give the same result.

Stack the melody against a transposed copy of itself. Four semitones up is a major third:

let melody = [C4 D4 E4 F4];
print(melody.stack(melody.transpose(4)).describe());

.transpose() leaves rests untouched, so a melody with gaps harmonizes cleanly.

Append a reversed copy:

let base = [C4 D4 E4 G4];
print(base.concat(base.reverse()).describe());

.rotate(n) is the other reordering: it shifts the notes right by n (negative shifts left) and wraps around the ends.

.degrade(p) drops each note with probability p. On a stream of hi-hats it adds occasional gaps:

use "std/instruments" { Sampler, Kit };
let hats = AudioTrack("hats");
hats.set_instrument(Sampler(Kit("TR-808")));
hats << [ch*8].degrade(0.15); // straight 8ths, about 15% of hits dropped
PLAY;

The same hits drop on every playback. To change which hits drop, change the pattern.

.map(fn) calls your function with each note’s MIDI number and plays what it returns: a number replaces the note, an array turns it into a chord, and a pattern substitutes a sub-sequence. Fold a melody into one octave:

let melody = [C4 D4 E4 F4 G4];
let folded = melody.map(fn(note) {
return 60 + (note % 12);
});
print(folded.describe());

Change notes by where they fall in the cycle

Section titled “Change notes by where they fall in the cycle”

.map_with_timing(fn) also passes each note’s start (0.0 to 1.0 in the cycle) and its duration. Lift the second half of the cycle up an octave:

let pat = [C4 D4 E4 F4];
let lifted = pat.map_with_timing(fn(note, start, dur) {
if start >= 0.5 { return note + 12; }
return note;
});
print(lifted.describe());

When a chain doesn’t sound the way you expect, print .describe() and read it inside-out. .length() counts the events in one cycle:

print([C4 D4 E4].reverse().transpose(7).describe());
// => Transpose(7, Reverse(Sequence[3]))
print([C4 D4 E4 F4].length());
// => 4

Transpose(7, Reverse(Sequence[3])) is a three-note sequence, reversed, then transposed. If the order is not what you meant, reorder the chain. How Patterns Work explains why the order matters.