Pattern Methods
Patterns support methods for transformation and manipulation. Methods return new patterns (patterns are immutable), enabling powerful method chaining.
Method Overview
Section titled “Method Overview”| Category | Methods | Purpose |
|---|---|---|
| Speed | .fast(), .slow() | Control playback rate |
| Transposition | .transpose() | Shift pitch by semitones |
| Reordering | .reverse(), .rotate() | Change element order |
| Combination | .cat() / .concat(), .stack() | Join or layer patterns |
| Degradation | .degrade() | Randomly drop notes |
| Euclidean | .euclid() | Euclidean rhythm distribution |
| Microtiming | .nudge(), .swing(), .humanize() | Per-step timing offsets |
| Indexing | .at() | Access individual elements |
| Mapping | .map(), .map_with_timing() | Transform each note |
| Iteration | .iter() | Create event iterator |
| Query | .length(), .describe() | Inspect patterns |
Speed Control
Section titled “Speed Control”Make the pattern play multiple times per cycle:
[C4 D4 E4].fast(2) // Pattern plays 2x per cycle[C4 D4 E4].fast(4) // Pattern plays 4x per cycleMake the pattern span multiple cycles:
[C4 D4 E4].slow(2) // Pattern spans 2 cycles[C4 D4 E4].slow(4) // Pattern spans 4 cyclesThese affect the whole pattern’s duration, not individual elements.
Timing Effects
Section titled “Timing Effects”For a 3-note pattern [C4 D4 E4]:
| Method | Events per cycle | Duration per event |
|---|---|---|
.fast(2) | 6 | 1/6 cycle |
.fast(0.5) | 1.5 | 1/1.5 cycle |
.slow(2) | 1.5 | 1/1.5 cycle |
.slow(4) | 0.75 | 1/0.75 cycle |
// Verify with .describe()PRINT [C4 D4 E4].fast(2).describe()// => "Fast(2, Sequence[3])"Transposition
Section titled “Transposition”Shift all notes by semitones:
let pat = [C4 E4 G4];
pat.transpose(12) // One octave up: [C5 E5 G5]pat.transpose(-12) // One octave down: [C3 E3 G3]pat.transpose(7) // Up a fifth: [G4 B4 D5]Rests pass through unchanged — they have no pitch to transpose.
Common Intervals
Section titled “Common Intervals”| Semitones | Interval |
|---|---|
| 1 | Minor 2nd |
| 2 | Major 2nd |
| 3 | Minor 3rd |
| 4 | Major 3rd |
| 5 | Perfect 4th |
| 7 | Perfect 5th |
| 12 | Octave |
Chaining transposition with other methods:
// Harmonize a melody in parallel thirdslet melody = [C4 D4 E4 F4];melody.stack(melody.transpose(4))Reordering
Section titled “Reordering”Reverse
Section titled “Reverse”Reverse the order of elements:
let pat = [C4 D4 E4 F4];pat.reverse() // [F4 E4 D4 C4]// Palindrome pattern: forward then backwardlet base = [C4 D4 E4 G4];base.cat(base.reverse())Rotation
Section titled “Rotation”Shift elements left (positive) or right (negative):
let pat = [C4 D4 E4 F4];
pat.rotate(1) // [D4 E4 F4 C4] — shift left by 1pat.rotate(2) // [E4 F4 C4 D4] — shift left by 2pat.rotate(-1) // [F4 C4 D4 E4] — shift right by 1Rotation results for [C4 D4 E4 F4]:
| Rotation | Result |
|---|---|
0 | [C4 D4 E4 F4] |
1 | [D4 E4 F4 C4] |
-1 | [F4 C4 D4 E4] |
2 | [E4 F4 C4 D4] |
// Cycle through all rotations of a patternlet pat = [C4 E4 G4 B4];let r0 = pat;let r1 = pat.rotate(1);let r2 = pat.rotate(2);let r3 = pat.rotate(3);Concatenation and Stacking
Section titled “Concatenation and Stacking”Concatenation
Section titled “Concatenation”Join patterns end-to-end:
let pat = [C4 D4];pat.cat([E4 F4]) // [C4 D4 E4 F4] — sequentialpat.concat([E4 F4]) // Same as .cat()// Build a longer phrase from fragments[C4 D4].cat([E4 F4]).cat([G4 A4])Stacking
Section titled “Stacking”Play patterns simultaneously:
let melody = [C4 E4 G4];melody.stack([G3 B3 D4]) // Both play at the same time// Three-layer stack[C4 E4 G4].stack([E3 G3 B3]).stack([C3*3])Degradation
Section titled “Degradation”Randomly drop notes from a pattern:
let pat = [C4 D4 E4 F4 G4 A4 B4 C5];
pat.degrade(0.25) // Light — 25% chance to drop each notepat.degrade(0.5) // Mediumpat.degrade(0.75) // HeavyUseful for humanizing hi-hats:
[C4*8].degrade(0.15) // Straight 8ths with occasional drops// Degrade combined with speed for glitchy textures[C4 D4 E4 F4].fast(4).degrade(0.4)Euclidean
Section titled “Euclidean”Apply Euclidean rhythm distribution to a pattern:
[C4].euclid(3, 8) // 3 hits spread over 8 steps[C4].euclid(5, 8) // 5 hits over 8 steps[C4].euclid(3, 8, 1) // With rotation offsetSee Euclidean Rhythms for more details.
Microtiming
Section titled “Microtiming”Microtiming methods shift individual events forward or backward in time without changing notes. All amounts are step-relative (fraction of the event’s step size), so the same value works regardless of pattern density. Original note durations are preserved.
.nudge(offsets)
Section titled “.nudge(offsets)”Apply per-step timing offsets. Each offset is a fraction of the event’s step size (0.0 = no shift, positive = forward, negative = backward):
[bd sd bd sd].nudge([0.0, 0.25, 0.0, -0.1])| Offset | Meaning |
|---|---|
0.0 | No shift |
0.25 | 25% of step forward |
-0.1 | 10% of step backward |
The offsets array cycles if the pattern has more events than offsets:
// Two offsets applied to four events: [0, shift, 0, shift][bd sd bd sd].nudge([0.0, 0.1])Events that shift outside the cycle boundary are filtered out.
.swing(amount)
Section titled “.swing(amount)”Classic swing feel. Even-indexed events stay on the grid, odd-indexed events shift forward by amount as a fraction of their step size.
[bd sd bd sd].swing(0.25) // moderate swing| Amount | Feel |
|---|---|
| 0.15 | Subtle shuffle |
| 0.25 | Moderate swing |
| 0.33 | Triplet feel (classic MPC) |
| 0.5 | Maximum / dotted feel |
.humanize(amount)
Section titled “.humanize(amount)”Deterministic random timing variation. Each event receives a unique offset derived from a hash of its start time, mapped to [-amount, +amount] of the event’s step size.
[bd sd bd sd].humanize(0.1) // +/- 10% of step jitterKey properties:
- Deterministic: same pattern always produces the same offsets
- Unique per event: each event’s offset differs, derived from its position
- Bounded: offsets never exceed
[-amount, +amount]of step size
| Amount | Feel |
|---|---|
| 0.05 | Barely perceptible |
| 0.1 | Subtle human feel |
| 0.15 | Noticeable but musical |
| 0.25 | Loose, relaxed timing |
Microtiming methods chain naturally:
// Swing for groove structure, then humanize for variation[bd sd bd sd].swing(0.25).humanize(0.05)See Microtiming for full groove recipes and layered examples.
Indexing
Section titled “Indexing”Access individual elements by index:
let pat = [C4 D4 E4 F4];pat.at(0) // First element as a patternpat[2] // Third element (same as .at(2))Works on all pattern types:
let chord = [C4, E4, G4, B4];chord[0] // Root
let alt = <C4 E4 G4>;alt[1] // Second alternativeIndexing can be chained with other methods:
[C4, E4, G4].at(0).transpose(12) // Root up one octaveMapping
Section titled “Mapping”Transform each note in a pattern with a function.
.map(fn)
Section titled “.map(fn)”The callback receives each MIDI pitch as a number:
let melody = [C4 D4 E4 F4 G4 A4 B4 C5];
// Melodic inversion around C4let pivot = 60;let inverted = melody.map(fn(note) { return pivot - (note - pivot);});
// Octave fold — constrain notes into C4-B4let folded = melody.map(fn(note) { return 60 + (note % 12);});Return Types
Section titled “Return Types”The callback can return different types:
// Number: replaces the notepat.map(fn(note) { return note + 12; });
// Array: produces stacked (simultaneous) noteslet chords = #[#[C4, E4, G4], #[D4, Gb4, A4]];[0 1].map(fn(s) { return chords[s]; });
// Pattern: replaces each note with a sub-patternpat.map(fn(note) { return [C4 E4 G4 C5]; });.map_with_timing(fn)
Section titled “.map_with_timing(fn)”The callback receives (note, start, duration) where start is the position within the cycle (0.0 to 1.0):
let pattern = [C4 D4 E4 F4 G4 A4 B4 C5];
// Silence the second half of each cyclelet silence = [_];let first_half = pattern.map_with_timing(fn(note, start, dur) { if start >= 0.5 { return silence; } return note;});
// Alternating octaves by positionlet alternating = pattern.map_with_timing(fn(note, start, dur) { let index = start * 8; if index % 2 < 1 { return note; } return note + 12;});Iteration
Section titled “Iteration”Create an infinite iterator over pattern events:
let pat = [C4 D4 E4];let events = pat.iter().take(8).collect(); // Always use .take()!See Iterators for full details.
Debugging with .describe()
Section titled “Debugging with .describe()”Get a human-readable description of a pattern’s structure:
PRINT [C4 D4 E4].describe()// => "Sequence[3]"
PRINT [C4, E4, G4].describe()// => "Stack[Sequence[1], Sequence[1], Sequence[1]]"
PRINT <C4 E4 G4>.describe()// => "Alternating[3]"Transformation Chains
Section titled “Transformation Chains”.describe() nests, showing the full transformation pipeline:
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].fast(2).degrade(0.3).describe()// => "Degrade(0.3, Fast(2, Sequence[3]))"Microtiming Descriptions
Section titled “Microtiming Descriptions”PRINT [C4 D4].swing(0.25).describe()// => "Swing(0.25, Sequence[2])"
PRINT [C4 D4].humanize(0.1).describe()// => "Humanize(0.1, Sequence[2])"
PRINT [C4 D4].nudge([0.0, 0.05]).describe()// => "Nudge([0.0, 0.05], Sequence[2])"Debugging Workflow
Section titled “Debugging Workflow”Use PRINT to inspect patterns at any point in a chain:
let base = [C4 D4 E4 F4];PRINT base.describe()// => "Sequence[4]"
let transformed = base.reverse().fast(2);PRINT transformed.describe()// => "Fast(2, Reverse(Sequence[4]))"
PRINT transformed.length()// => 4Length
Section titled “Length”Count the number of elements in a pattern:
let pat = [C4 D4 E4 F4];pat.length() // 4Method Chaining
Section titled “Method Chaining”Methods return new patterns, enabling chaining:
[C4 D4 E4] .reverse() .transpose(12) .fast(2)// Result: plays reversed+transposed pattern 2x per cycleOrder Matters
Section titled “Order Matters”The order of operations affects the result:
[C4 D4].concat([E4]).reverse() // [E4 D4 C4][C4 D4].reverse().concat([E4]) // [D4 C4 E4]How order changes the output for [C4 D4 E4]:
| Chain | Result |
|---|---|
.reverse().fast(2) | Reversed pattern, played 2x per cycle |
.fast(2).reverse() | Sped-up pattern, then reversed |
.transpose(12).degrade(0.5) | Transposed first, then notes dropped |
.degrade(0.5).transpose(12) | Notes dropped first, survivors transposed |
Practical Recipes
Section titled “Practical Recipes”// Swung hi-hats with occasional drops[C4*8].degrade(0.15).swing(0.2)
// Rising arpeggio that speeds up[C4 E4 G4 C5].transpose(12).fast(2)
// Groove-locked bass pattern[C3 _ C3 G2].swing(0.25).humanize(0.05)
// Evolving chord sequencelet chord = [C4, E4, G4];chord.rotate(1).transpose(5).slow(2)Method Comparison Tables
Section titled “Method Comparison Tables”Speed Methods
Section titled “Speed Methods”| Method | Equivalent compact notation | Effect |
|---|---|---|
.fast(2) | [C4 D4]*2 | Double playback speed |
.fast(0.5) | [C4 D4]/2 | Half playback speed |
.slow(2) | [C4 D4]/2 | Half playback speed |
.slow(0.5) | [C4 D4]*2 | Double playback speed |
Timing Methods
Section titled “Timing Methods”| Method | Control | Deterministic | Use case |
|---|---|---|---|
.nudge(offsets) | Per-step array | Yes | Custom groove templates |
.swing(amount) | Single value | Yes | Classic swing feel |
.humanize(amount) | Single value | Yes (hash-based) | Natural timing variation |
Structural Methods
Section titled “Structural Methods”| Method | Changes notes? | Changes timing? | Changes order? |
|---|---|---|---|
.transpose() | Yes | No | No |
.reverse() | No | No | Yes |
.rotate() | No | No | Yes |
.fast() / .slow() | No | Yes | No |
.degrade() | Yes (drops) | No | No |
.nudge() / .swing() / .humanize() | No | Yes | No |
.map() | Yes | No | No |
Practical Examples
Section titled “Practical Examples”Building Variations
Section titled “Building Variations”let base = [C4 E4 G4];let patterns = Array( base, base.transpose(5), // F major base.transpose(7), // G major base.transpose(5) // Back to F);Groove-Locked Drums
Section titled “Groove-Locked Drums”let kick = [C4 _ C4 _].swing(0.25);let hat = [C4*8].degrade(0.15).humanize(0.015);let snare = [_ C4 _ C4].nudge([0.0, 0.04]);Pattern Builder Function
Section titled “Pattern Builder Function”fn arpeggio(base) { return base.concat(base.reverse());}
let cArp = arpeggio([C4 E4 G4]);Full Musical Example
Section titled “Full Musical Example”// Build a 4-bar phrase with method chaininglet motif = [C4 D4 E4 G4];
let phrase = motif .cat(motif.transpose(5)) // Add F major variation .cat(motif.rotate(2)) // Add rotated variation .cat(motif.reverse()) // End with retrograde .slow(4); // Spread over 4 cycles
// Add groovelet grooved = phrase.swing(0.2).humanize(0.05);See Also
Section titled “See Also”- Pattern Basics — Pattern fundamentals
- Nested & Parallel Patterns — Pattern structure
- Compact Notation —
*and/speed notation - Euclidean Rhythms — Euclidean rhythm details
- Iterators — Iterate over pattern events
- Microtiming — Full groove recipes
- Pattern Methods Reference — Complete method signatures