Transforming Patterns
Once you have a pattern, you rarely leave it alone. You speed it up, flip it
around, transpose it, layer it against itself. In Resonon you do this with
methods — .fast(), .transpose(), .reverse(), and friends — and you
chain them together to build a phrase one transformation at a time.
Every method returns a new pattern and leaves the original untouched. Think of it like non-destructive editing in a DAW: the source clip never changes, you just stack edits on top. That means you can derive a dozen variations from one motif without ever clobbering it.
Here’s where we’re headed — a four-note motif, flipped and doubled, played through a melodic sampler:
use "std/instruments" { SamplerMelodic, Kit };
let lead = AudioTrack("lead");lead.load_instrument(SamplerMelodic(Kit("keys")));
lead << [C4 E4 G4 C5].reverse().fast(2);
PLAY;Reading the chain left to right: start with the arpeggio [C4 E4 G4 C5],
.reverse() it so it descends, then .fast(2) so the whole thing plays twice
per cycle. The rest of this chapter walks through the transforms you’ll reach for
most.
.fast(n) plays the pattern n times per cycle; .slow(n) stretches it across
n cycles. They’re inverses — .slow(2) is exactly .fast(0.5).
let pat = [C4 D4 E4];pat.fast(2); // plays twice per cyclepat.slow(2); // spans two cyclesThese change the pattern’s overall timing, not the order or pitch of its notes.
They’re the method form of the * and / modifiers from
mini-notation — [C4 D4 E4].fast(2) and
[[C4 D4 E4]*2] produce the same thing.
.transpose(semitones) shifts every note by a number of semitones — positive up,
negative down. Rests have no pitch, so they pass through untouched.
let pat = [C4 E4 G4];pat.transpose(12); // up an octave: [C5 E5 G5]pat.transpose(-12); // down an octave: [C3 E3 G3]pat.transpose(7); // up a fifth: [G4 B4 D5]A handy trick: stack a pattern against a transposed copy of itself to harmonize it. Up four semitones is a major third:
let melody = [C4 D4 E4 F4];melody.stack(melody.transpose(4));.reverse() plays the notes back to front. .rotate(n) shifts them left by n
(negative shifts right), wrapping around the ends.
let pat = [C4 D4 E4 F4];pat.reverse(); // [F4 E4 D4 C4]pat.rotate(1); // [D4 E4 F4 C4]pat.rotate(-1); // [F4 C4 D4 E4]Rotation wraps with modulo, so on a 4-note pattern .rotate(4) is the original
and .rotate(5) equals .rotate(1). A reversed copy concatenated onto the
original gives you a palindrome:
let base = [C4 D4 E4 G4];base.cat(base.reverse());Combining patterns
Section titled “Combining patterns”.cat() joins patterns end to end (sequential); .stack() layers them so they
sound at the same time (simultaneous). .concat() is an alias for .cat().
let a = [C4 D4];a.cat([E4 F4]); // [C4 D4 E4 F4] — one after the othera.stack([G3 B3]); // both play together.cat() is the method form of writing patterns in sequence; .stack() is the
method form of the comma in [C4, E4, G4]. Both chain, so you can build up
layers:
[C4 E4 G4].stack([E3 G3 B3]).stack([C3*3]);Thinning out
Section titled “Thinning out”.degrade(probability) randomly drops notes, where probability is the chance
each note is removed. It’s perfect for loosening up a busy part — a stream of
hi-hats with the occasional gap:
[C4*8].degrade(0.15); // straight 8ths, ~15% of hits droppedTransforming each note
Section titled “Transforming each note”.map(fn) runs a function over every note’s MIDI number and uses what you return
in its place. This is the escape hatch — anything you can compute, you can apply
per note.
let melody = [C4 D4 E4 F4 G4];
// Octave fold — wrap everything into the C4–B4 rangemelody.map(fn(note) { return 60 + (note % 12);});Return a number to replace the note, an array to turn it into a chord, or a
pattern to substitute a whole sub-sequence. For timing-aware transforms,
.map_with_timing(fn) hands your callback (note, start, duration) where
start is the note’s position in the cycle from 0.0 to 1.0:
let pat = [C4 D4 E4 F4];
// Lift the second half of the cycle up an octavepat.map_with_timing(fn(note, start, dur) { if start >= 0.5 { return note + 12; } return note;});Inspecting a pattern
Section titled “Inspecting a pattern”When a chain doesn’t sound right, .describe() shows you exactly what you built,
and .length() counts the events in one cycle:
PRINT [C4 D4 E4].fast(2).describe();// => Fast(2, Sequence[3])
PRINT [C4 D4 E4].reverse().transpose(7).describe();// => Transpose(7, Reverse(Sequence[3]))
PRINT [C4 D4 E4 F4].length();// => 4Order matters
Section titled “Order matters”Because each method wraps the result of the previous one, the order of a chain changes the outcome:
[C4 D4].concat([E4]).reverse(); // [E4 D4 C4][C4 D4].reverse().concat([E4]); // [D4 C4 E4]In the first, you reverse the whole three-note sequence; in the second, you
reverse just [C4 D4] and then append E4. When a chain surprises you,
.describe() it and read inside-out to see what happened.
See Also
Section titled “See Also”- Mini-Notation in Depth — the
*and/modifiers behind.fast()/.slow() - Polymetric & Euclidean —
.euclid()and Euclidean rhythm distribution - Iterators — step through pattern events with
.iter()
Next Steps
Section titled “Next Steps”Transforms reshape what plays. Next, shape exactly when it plays.
- Microtiming — swing, nudge, and humanize for groove