Skip to content

Mini-Notation in Depth

Mini-notation is the compact syntax inside pattern brackets. With a handful of characters you can subdivide time, layer voices, vary patterns per cycle, and shape rhythms — the same way you’d program a step sequencer, but with text. This chapter walks through each technique with timing breakdowns; for the terse syntax tables, see the Mini-Notation Grammar reference.

Here’s where we’re headed — a beat using everything this chapter covers:

use "std/instruments" { Sampler, Kit };
let drums = AudioTrack("drums");
drums.load_instrument(Sampler(Kit("TR-808")));
drums << [bd <sd [sd sd]>, ch*8, <_ oh>];
PLAY;

Three layers separated by commas: a kick with a snare that varies between single and double hits each cycle, eight hi-hats, and an open hat sounding every other cycle. By the end of this page you’ll read patterns like this at a glance.

Put a pattern inside a pattern to subdivide time. The inner pattern squeezes into the slot the outer pattern gives it:

let p = [[C4 D4] E4];
// C4: 0 - 1/4, D4: 1/4 - 1/2, E4: 1/2 - 1

The outer pattern has two slots, each half a cycle. The inner [C4 D4] divides its half into two quarters. In DAW terms: the bar splits in half, and the first half splits again.

This rule applies recursively at every level:

duration = parent_slot_duration / number_of_siblings

For [[[C4 D4] E4] F4]:

NoteNesting depthCalculationDuration
F41 (outer)1 / 21/2 cycle
E42 (middle)1/2 / 21/4 cycle
C43 (inner)1/4 / 21/8 cycle
D43 (inner)1/4 / 21/8 cycle

Nested groups don’t need matching sizes — each group divides its own slot independently:

let uneven = [[C4 D4 E4] [F4 G4]];
// First half: 3 notes, 1/6 cycle each
// Second half: 2 notes, 1/4 cycle each

This naturally produces mixed groupings — triplets against duplets, quintuplets against pairs — without any special syntax.

A comma splits a pattern into voices that play simultaneously:

let chord = [C4, E4, G4]; // C major chord — all notes at once

With one element per voice, all onsets align and you get a chord. But each voice keeps its own time grid, based on its own element count:

let voices = [C4 D4, E4 F4 G4];
// Voice 1: 2 elements, onsets at 0 and 1/2
// Voice 2: 3 elements, onsets at 0, 1/3, 2/3
TimeVoice 1Voice 2
0C4E4
1/3F4
1/2D4
2/3G4

Five separate onsets in one cycle — a polyrhythmic texture, not a chord. The classic 3-against-2:

let three_two = [bd bd bd, sd sd];
// Kicks every 1/3, snares every 1/2 — only the downbeat aligns

Parallel voices are how you write drum patterns, with each instrument on its own layer:

use "std/instruments" { Sampler, Kit };
let drums = AudioTrack("drums");
drums.load_instrument(Sampler(Kit("TR-808")));
drums << [bd _ bd _, _ sd _ sd, ch ch ch ch];
// Kick on 1 and 3, snare on 2 and 4, steady hats
PLAY;

Each voice can use nesting independently — every layer gets its own subdivision structure:

let layered = [[C4 D4] E4, G4 [A4 B4]];

Angle brackets <...> play one element per cycle, rotating through the list:

let alt = <C4 D4 E4>;
// Cycle 0: C4, cycle 1: D4, cycle 2: E4, cycle 3: C4, ...

The selection is simply cycle_number % number_of_elements, and the chosen element fills the entire slot the <...> occupies. Compare:

PatternBehavior
[bd sd hh]All three play, every cycle
<bd sd hh>One per cycle, rotating

Alternation is your tool for variation over time. Alternate whole phrases:

let phrases = <[C4 E4 G4 E4] [C4 F4 A4 F4]>; // Two bars, looping A-B

Or nest <...> inside a sequence so one slot varies while the rest stays put:

let fills = [bd <sd [sd sd]>];
// Cycle 0: bd sd — cycle 1: bd sd-sd (doubled snare)
let every_other = <C4 _>; // Note every second cycle only

Modifiers are postfix shorthand attached to a single element. There are three, and an element takes at most one:

ModifierEffectExampleEquivalent
*nPlay n times within the slot (faster)[bd*2 sd][[bd bd] sd]
@nWeight: n× share of the cycle (longer)[bd@2 sd]bd gets 2/3, sd 1/3
!nReplicate: n separate slots at normal speed[bd!2 sd][bd bd sd]

*n squeezes n repetitions into one slot — the slot count stays the same, the element just plays faster:

let hats = [bd sd*2]; // bd in first half; sd twice in second half

Decimals are allowed: *1.5 plays the element one-and-a-half times, cutting the last repetition short.

@n changes how the cycle is shared. Weights are relative — the cycle divides proportionally to the total:

PatternWeightsDistribution
[bd@2 sd]2 + 1 = 3bd: 2/3, sd: 1/3
[bd@3 sd@1]3 + 1 = 4bd: 3/4, sd: 1/4
[a@1 b@2 c@1]1 + 2 + 1 = 4a: 1/4, b: 1/2, c: 1/4

Each element still plays once — just for longer or shorter. Decimals work here too. A common use is a held note before a quick turn:

let phrase = [C4@3 D4]; // Long C4, quick D4 pickup

!n writes the element n times, as if you’d typed it out — n separate slots at normal speed:

let four_kicks = [bd!4]; // Same as [bd bd bd bd]

Unlike *n, which speeds up inside one slot, !n adds slots. Replicate only accepts integers.

Three more postfixes attach to individual elements. Each is covered in depth elsewhere; here’s the shape of them:

Velocity ^v sets how hard a note is played (0–127):

let accents = [C4^120 C4^60 C4^60 C4^60]; // Accent on beat 1

Chords Root:Quality put whole chords in a pattern, with optional inversions:

let progression = [D4:min7 G4:7 C4:maj7~1]; // ii-V-I, last chord inverted

Sample methods transform individual hits, and chain:

let beat = [bd.vel(1.2) sd.pitch(-5).pan(0.5)];

Postfix parts combine in a fixed order: velocity, then euclidean parameters, then modifier — C4^80(3,8)*2. See the grammar reference for the full token table, Music Theory for chord qualities and the chord() function, and Sampler & Sample for all sample methods.

Back to the beat from the top of the page:

let beat = [bd <sd [sd sd]>, ch*8, <_ oh>];

Read it comma by comma — three parallel voices:

VoicePatternBehavior
1bd <sd [sd sd]>Kick, then a snare slot that alternates per cycle
2ch*8Eight closed hats per cycle
3<_ oh>Open hat every other cycle

On even cycles voice 1 plays bd sd; on odd cycles the snare slot holds [sd sd], a doubled hit in the same half-cycle. Voice 3 alternates between silence and an open hat. Every technique composes with every other — nesting inside alternation inside parallel voices, to any depth.

use "std/instruments" { Sampler, Kit };
let drums = AudioTrack("drums");
drums.load_instrument(Sampler(Kit("TR-808")));
drums << [<bd [bd bd]> sd, ch!4, <_ _ oh _>];
// Kick alternates single/double, snare on the backbeat,
// four hats, open hat every fourth cycle
PLAY;

Two more rhythm-generation tools build on this notation, and a method API transforms any pattern in code.