Skip to content

Patterns

A Pattern is the central contract in barline. It is anything with a query method that returns the notes inside a time window — pure and deterministic, so the visualization always matches playback. The constructors below build the common cases; for the full interface and writing your own, see Custom patterns.

All of these are imported from @barline/core:

import { r, grid, at, backbeat, euclidean, generate, notes, beats } from "@barline/core";

Patterns loop: a one-bar pattern repeats every bar until the section ends.

The primary dense-rhythm syntax. A string of steps, evaluated left to right.

r`x . . . x . . . x . . . x . . .` // four-on-the-floor, 16 sixteenths
r`x . [x x x] .` // a triplet fill squeezed into step 3
r`${1} . ${0.6} .` // accents via interpolated velocities
r`x*4 . . . .` // x*4 expands to four hits

The grammar is deliberately minimal — new expressive power goes through interpolation or helpers, never new string syntax:

TokenMeaning
xa hit at full velocity
. or ~a rest
x*4repeat: four hits (.*4 / ~*4 for rests)
[x x x]subdivision: fit the contents into the space of one step
${0.6}an interpolated number → a hit at that velocity (0 = rest)

Each step defaults to a 16th note (0.25 beats). Interpolated values must be velocities in [0, 1].

grid(opts) returns a tagged template just like r, but with a fixed note, velocity, and step length applied to every hit.

grid({ note: "F1", velocity: 0.95 })`. x x x . x x x . x x x . x [x x]`
grid({ note: "C3", step: beats(0.5) })`x . x . x . x .`

GridOptions is { step?: Beats; note?: Note; velocity?: number }. The default step is beats(0.25); the default note is middle C.

Sparse rhythms by index. at(indices, of = 16, opts?) places hits at the given indices within a grid of of steps.

at([2, 6, 10, 14]) // offbeat hats in a 16-step bar
at([0, 8]) // two hits, on the 1 and the 3
at([0], 16) // one downbeat per bar — a pulse, not a groove

Shorthand for at([4, 12], 16, opts).

clap.play(backbeat({ velocity: 0.9 }));

euclidean(hits, stepCount, opts?) spreads hits as evenly as possible across stepCount steps — the rhythm behind countless techno stabs and percussion lines.

euclidean(3, 16, { note: "F3" }) // 3 hits over 16 steps
euclidean(5, 16, { note: "Ab3", velocity: 0.85 })

Stack a euclidean rhythm across chord tones for instant stabs:

import { stack } from "@barline/core";
function chordStabs(hits: number, steps: number, chord: readonly string[]) {
return stack(...chord.map((note) => euclidean(hits, steps, { note, velocity: 0.85 })));
}
const stabsA = chordStabs(3, 16, ["F3", "Ab3", "C4"]); // F minor triad

The functional escape hatch. generate(stepCount, fn, opts?) calls fn(index, ctx) for each step and decides per step whether it hits and at what velocity.

// Same seed strings → playback and visualization always agree.
const scatter = generate(16, (i, ctx) => ({
hit: i % 4 === 2 || ctx.rng(`scatter:${i}`) < 0.18,
velocity: 0.35 + 0.45 * ctx.rng(`vel:${i}`),
}));
// A crescendo roll: velocity climbs across the bar.
const crescRoll = generate(16, (i) => ({
hit: true,
velocity: Math.min(1, 0.25 + i * 0.05),
}));

fn returns { hit: boolean; velocity?: number }. Use ctx.rng(seed) for any randomness — never Math.random() — so the result is deterministic.

notes(sequence, step = beats(0.25)) loops a sequence of pitches. null is a rest.

const bassline = notes(["F1", null, "F1", "F1", null, "F1", "Ab1", "Eb1"], beats(0.5));
// A one-bar 303-ish acid line at 16th notes:
const acid303 = notes(
["F2", "F3", "F2", "Ab2", "F2", "F3", "Eb3", "F2",
"F2", "F3", "C3", "Ab2", "F2", "Bb2", "F3", "Eb3"],
beats(0.25),
);

Pitches are note names ("F1", "Ab3", "C4") — see Types for the Note / NoteName model. The piano roll writes back into a plain notes([…]) literal, so dragging a note edits this array directly (see Clip detail).

Every constructor returns a Pattern, and patterns compose with free-function combinatorsstack, sequence, slow, fast, gain, chance, swing, humanize. They are functions, not methods, so your own pattern classes don’t need to implement dozens of chaining methods.