Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Pattern functions

Every function that makes or changes a pattern, with its parameters, what it accepts, the errors it raises and an example. Methods are called on a pattern (s("bd*4").fast(2)) and return a new pattern; the original is unchanged.

Many parameters accept a number, pattern string or pattern: gain(0.8), gain("1 0.6") and gain(seq("1 0.6")) are the same kind of argument. Anything else raises expected a number, a pattern string or a pattern. How a second pattern is read is explained in Reading one pattern from another.

Constructors

s() and sound()

s(pattern)        // number, pattern string or pattern
sound(pattern)    // the same

A pattern of sound names: drums, synth presets, sampled instruments, kit sounds such as tr808:sd (see Sounds). Each word is one hit; a number after the last colon picks a variation (bd:3, tr808:sd:2). A track whose sound pattern uses several names is split into one track per name (drums.bd, drums.hh …) so each gets its own mixing role.

blank("house");
track("drums", s("bd*4, [~ cp]*2, [~ hh]*4"));

note()

note(pattern)     // note names or MIDI numbers

A pattern of pitches: c3, eb2, f#4, or MIDI numbers (60 is middle C). A name without an octave is in octave 3. The instrument comes from .s(); without it, the pluck synth plays. See Pitch.

Errors: a word that is not a note name, when the track is made: cannot read 'h3' as a note (names with an octave like c3, f#4, bb2, or MIDI numbers).

blank("house");
track("bass", note("<a1 f1 c2 g1>").struct("x ~ ~ x ~ x ~ ~").s("sub_bass"));

n()

n(pattern)        // scale degrees, from 0

A pattern of scale degrees in the scale set by .scale(), or the song’s key when there is none. Degree 0 is the tonic in octave 3 (.octave() changes it); 7 in a seven-note scale is the tonic an octave up; negative degrees go down. Without .s(), the pluck synth plays. See Pitch.

Errors: a word instead of a number, when the track is made: n() takes scale degrees (0 1 2 -1 ...), not 'e'.

blank("house");
key("A minor");
track("lead", n("0 2 4 7 4 2").octave(4).s("pluck"));

chord()

chord(pattern)    // chord symbols, Roman numerals, prog, prog:NAME

A pattern of chords: names (Am7), slash chords (F/A), Roman numerals (ii7, V7/V). Each event plays every note of its chord, voiced with voice leading (see Voicing), or only its root with .root(). prog plays the song’s progression for the section; prog:B always plays progression B (or A when the song has no B). Without .s(), the epiano synth plays.

Errors: a word that is neither, when the track is made: cannot read 'Xyz' as a chord (names like Am7, Fmaj7, Bb, numerals like ii7, bVII, V7/V, or prog).

blank("house");
key("A minor"); prog("A", "Am7 Fmaj7 Dm7 Em7"); bars_per_chord(1);
track("keys", chord("prog").struct("x ~ ~ x ~ ~ x ~").s("epiano"));

seq()

seq(pattern)      // plain values

A pattern of plain numbers and words, for arguments: .gain(seq("1 0.6")), .struct(seq("x ~ x x")). A string argument does the same job, so seq() is mostly useful for building an argument once and reusing it.

stack()

stack([pattern, pattern, ...])

Plays patterns together. The result has the kind and track settings of the first pattern, so stack patterns of one kind.

Errors: stack() takes patterns when an item is not a pattern; stack() needs at least one pattern for [].

blank("house");
track("drums", stack([s("bd*4"), s("[~ cp]*2"), s("hh*8").gain(0.5)]));

cat()

cat([pattern, pattern, ...])

One pattern per bar, in turn: bar 1 plays the first, bar 2 the second, and so on, then again. Like stack(), it takes the kind and track settings of the first pattern. Errors as for stack(), with cat() in the message.

blank("house");
track("drums", cat([s("bd*4, hh*8"), s("bd*4, hh*8"), s("bd*4, hh*8"), s("bd*2 [bd*4], hh*16")]));

silence()

silence()

A pattern with no events.

Time

fast() and slow()

pattern.fast(factor)     // number above 0, up to 256
pattern.slow(factor)     // number above 0, up to 256

fast(2) plays the pattern twice per bar; slow(2) spreads one cycle over two bars. Factors can be decimals.

Errors: fast needs a number; fast takes a number above 0 and up to 256, not 1000; the same for slow.

blank("house");
track("hats", s("hh hh hh oh").fast(2));          // eighth notes, an open hat on the "and" of 2 and 4
track("pad", chord("<Am F>").slow(2).s("pad"));     // each chord for two bars

late() and early()

pattern.late(cycles)     // number; negative moves earlier
pattern.early(cycles)    // number; negative moves later

Shifts the pattern in time. late(0.25) is one beat; small values such as late(0.02) make a part sit lazily behind the beat. At a section’s start, a shifted pattern brings in the end of the bar before it.

Errors: late needs a number, early needs a number.

blank("lofi");
track("keys", chord("<Dm9 G9>").s("epiano").late(0.02));

rev()

pattern.rev()

Reverses each bar: the last event comes first.

blank("house");
track("lead", n("0 1 2 4").scale("A:minor").octave(4).every(2, |x| x.rev()).s("pluck"));

ply()

pattern.ply(count)       // integer, 1 to 256

Repeats each event count times inside its own slot: s("bd sd").ply(2) is s("[bd bd] [sd sd]").

Errors: ply takes a number above 0 and up to 256, not 300; ply takes a whole number from 1 to 256, not 1.5.

blank("trap");
track("hats", s("hh*8").lastOf(4, |x| x.ply(2)));

Higher-order functions

These take a closure |x| ... that receives the pattern and returns a changed one. Track settings written inside a closure (|x| x.s("piano")) have no effect: only the events it returns are used.

every() and lastOf()

pattern.every(n, f)      // n: integer (below 1 acts as 1), f: closure
pattern.lastOf(n, f)

every(n, f) uses f’s version in the first bar of every n, counted from the start of the section (bars 1, 5, 9 … for n = 4). lastOf(n, f) uses it in the last bar of every n (bars 4, 8, 12 …), which is where a fill belongs.

blank("house");
track("drums", s("bd*4, [~ cp]*2").lastOf(4, |x| x.fast(2)));   // a doubled bar before each phrase

sometimes() and its relatives

pattern.sometimes(f)       // probability 0.5
pattern.often(f)           // 0.75
pattern.rarely(f)          // 0.25
pattern.almostNever(f)     // 0.1
pattern.almostAlways(f)    // 0.9
pattern.sometimesBy(p, f)  // p: number; 0 never, 1 always

Decides once for each event of the pattern, with the given probability, whether f applies to it, the way Strudel does. The chosen events go through f and the others play as they are, so each event is either itself or f’s version of it, never both: s("hh*16").sometimes(|x| x.late(0.01)) plays sixteen hats, some nudged late, and .sometimes(|x| x.ply(2)) doubles some events and leaves the others single. A transform that changes speed (fast, slow) is the exception: it picks events in its own time, so its copies can land on top of originals. The choice is random but repeatable (see Randomness).

Errors: sometimesBy needs a number.

blank("house");
track("hats", s("hh*16").gain(0.6).sometimes(|x| x.gain(0.3)));         // accents vary hit by hit
track("ride", s("rd*8").sometimes(|x| x.ply(2)).gain(0.5));              // some eighths become two sixteenths
track("lead", n("0 2 4 2").scale("A:minor").octave(4).rarely(|x| x.add(7)).s("pluck"));

off()

pattern.off(cycles, f)   // cycles: number; f: closure

Plays the pattern and, on top of it, f’s version shifted later by cycles. A classic is an echo an eighth note behind, an octave up.

Errors: off needs a number.

blank("house");
track("arp", n("0 2 4 6").scale("A:minor").octave(4).off(0.125, |x| x.add(7)).s("pluck"));

superimpose()

pattern.superimpose(f)

Plays the pattern and f’s version together, unshifted. Use it for doublings: a third or an octave above.

blank("house");
track("lead", n("0 1 2 4").scale("A:minor").octave(4).superimpose(|x| x.add(2)).s("pluck"));

degrade() and degradeBy()

pattern.degrade()        // drops each event with probability 0.5
pattern.degradeBy(p)     // p: number; 0 keeps all, 1 drops all

Drops events at random, repeatably. degradeBy(0.3) keeps about 70 % of the events. The mini-notation ? does the same per step.

Errors: degradeBy needs a number.

blank("house");
track("shaker", s("shaker*16").degradeBy(0.3).gain(0.5));

Structure

struct()

pattern.struct(rhythm)   // number, pattern string or pattern

Takes the timing from rhythm and the values from the pattern. Every “on” event of the rhythm (any non-zero number, any word but 0, false, f) becomes an event with the value the pattern has at that moment. Rests in the rhythm are silent. Use it to give a melody or a chord progression a rhythm without rewriting it.

blank("house");
track("stabs", chord("<Am7 Dm7>").struct("~ x ~ x ~ x ~ [x x]").s("stab"));

mask()

pattern.mask(onoff)      // number, pattern string or pattern

Keeps the events of the pattern that start where onoff is on, and removes the others. Unlike struct(), it never adds events or changes timing.

blank("house");
track("hats", s("hh*16").mask("<1 [1 0] 1 [1 0 0 1]>"));   // the hats drop out in places, bar by bar

euclid() and euclidRot()

pattern.euclid(k, n)         // integers: k hits over n slots
pattern.euclidRot(k, n, r)   // and rotated left by r slots

The pattern played on a euclidean rhythm. s("rim").euclid(5, 16) is s("rim(5,16)"). n is 1 to 256, as in mini-notation (otherwise a euclidean rhythm has 1 to 256 steps, not 1000); k is held between 0 and n; r wraps around, so a negative r rotates right.

blank("techno");
track("rim", s("rim").euclidRot(5, 16, 2).gain(0.6));

Per-event controls

Each control is read from its argument at the start of every event, so a pattern string gives different values in different places.

gain() and velocity()

pattern.gain(amount)       // number, pattern string or pattern; 0..1
pattern.velocity(amount)

How hard each note is played. Both set the same thing; when both are present, velocity wins. Without either, sound patterns play at 0.9 and the others at 0.8. Values above 1 play as 1. On synthesized drums and audio files, the level follows velocity to the power 1.6, so 0.5 sounds about 10 dB below 1; synth presets and sampled instruments respond the way the instrument does, with tone as well as level (a sampled piano switches to softer recordings).

blank("house");
track("hats", s("hh*16").gain("[0.4 0.25 0.8 0.25]*4"));   // accent on the off-beat eighth

legato()

pattern.legato(fraction)   // number, pattern string or pattern; 1 = the full slot

How long each note lasts, as a fraction of its slot. 0.5 is staccato, 1 (the default) is the whole slot, and values above 1 overlap the next note. On the mono synths with glide (808, acid, reese, square_lead) an overlap makes the pitch slide into the next note; sub_bass and wobble tie the notes without starting the envelope again. Arpeggiated notes default to 0.9.

blank("house");
track("bass", note("a1*8").s("organ_bass").legato("0.4 0.9"));

add() and transpose()

pattern.add(amount)        // number, pattern string or pattern; rounded to a whole number
pattern.transpose(amount)  // the same, in semitones always

Both move pitches. transpose() always counts semitones. add() adds to the pattern’s values, so what one step means depends on the pattern:

patternadd(1) movestranspose(1) moves
note()one semitone upone semitone up
n()one scale degree up: add(7) is an octave in a seven-note scale, add(5) in a pentatonic onethe played note one semitone up
chord()the voicing range one semitone up (voice leading still picks the notes)every voiced note one semitone up
chord().root()the root one semitone upthe root one semitone up
s()the sample or drum sound one semitone upthe same

n("0 2 4").transpose(12) is the same line an octave up in any scale; n("0 2 4").add(7) is too, in a seven-note scale. Several calls add up.

blank("house");
key("A minor");
track("lead",  n("0 2 4 2").octave(4).add("<0 0 -2 -1>").s("pluck"));       // the motif moves by scale steps
track("lead2", n("0 2 4 2").octave(4).transpose(12).s("bell").level(-6));   // the same line an octave up
track("perc",  s("rim*8").transpose("0 7 12 7").gain(0.5));

n() on a sound pattern

pattern.n(index)           // number, pattern string or pattern

On an s() pattern, picks the variation of each sound, like bd:3. It replaces an index written in the word.

blank("house");
track("kick", s("bd*4").bank("tr909").n("0 0 3 0"));

Track settings

These describe the track rather than single events, so they apply to the whole track wherever they appear in the chain, and a later call replaces an earlier one. Only the pattern given to track() counts: settings inside a closure are ignored.

methodwhat it setsvalues
.s(name), .sound(name)the instrument of a note(), n(), chord() or seq() patterna sound name or a file path
.bank(name)the drum machine or kit for the names in an s() patterntr909, tr808, tr707, acoustic, uzu, apple …
.scale(name)the scale for n(), and the key for Roman numerals in chord()"A:minor", "D dorian", "E:minor:pentatonic", "C:blues"
.octave(n)the octave of degree 0 in n(), of note names without an octave, and of .root() notesinteger; default 3, 2 for .root()
.voicing(), .voicing(lo, hi)the range chord voicings stay inMIDI integers, default 52, 74
.arp(mode)arpeggiate each chord event"up", "down", "updown"
.root()a chord() pattern plays only each chord’s root (its slash bass, when it has one)octave 2 unless .octave(n) says otherwise
.role(name)the mixing rolekick, snare, bass, chords, pad, lead …
.level(db)level in dB relative to the role’s targetnumber
.pan(x)stereo position−1 left .. 1 right
.room(x), .reverb(x)reverb send0..1
.delay(x)delay send0..1
.duck(x)sidechain depth from the kick0..1; default 0.3, 0 on drums
.lpf(hz), .cutoff(hz)a lowpass filter on the trackHz
.hpf(hz)a highpass filter on the trackHz
.crush(bits)a bitcrusherbits, 1..24
.distort(db)tanh saturationdrive in dB

.scale() and .arp() check their argument: unknown scale 'Q:minor' (try "A:minor", "C:major", "D:dorian", "E:minor:pentatonic"), unknown arp 'sideways' ("up", "down" or "updown"). The numeric settings raise <name> needs a number for anything else, and are not clamped by the pattern; Mixing functions says what the mixer does with them. .s() on an s() pattern does nothing, since the words already name the sounds.

blank("deep_house");
track("keys", chord("<Fm9 Bbm9>").voicing(53, 72).s("epiano")
                .level(-2).pan(-0.2).room(0.3).delay(0.1).lpf(4000));

.root() turns a chord pattern into a bass line that follows it. On the song’s progression, the bass changes when the progression does:

blank("house");
key("A minor"); prog("A", "Am F/A C G"); bars_per_chord(1);
track("keys", chord("prog").struct("x ~ ~ x ~ ~ x ~").s("epiano"));
track("bass", chord("prog").root().struct("x ~ ~ x ~ x ~ ~").s("sub_bass"));   // A2 A2 C2 G2