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.
r — the tagged-template grid
Section titled “r — the tagged-template grid”The primary dense-rhythm syntax. A string of steps, evaluated left to right.
r`x . . . x . . . x . . . x . . .` // four-on-the-floor, 16 sixteenthsr`x . [x x x] .` // a triplet fill squeezed into step 3r`${1} . ${0.6} .` // accents via interpolated velocitiesr`x*4 . . . .` // x*4 expands to four hitsThe grammar is deliberately minimal — new expressive power goes through interpolation or helpers, never new string syntax:
| Token | Meaning |
|---|---|
x | a hit at full velocity |
. or ~ | a rest |
x*4 | repeat: 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 — r carrying a note and options
Section titled “grid — r carrying a note and options”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.
at — hits at specific step indices
Section titled “at — hits at specific step indices”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 barat([0, 8]) // two hits, on the 1 and the 3at([0], 16) // one downbeat per bar — a pulse, not a groovebackbeat — clap/snare on 2 and 4
Section titled “backbeat — clap/snare on 2 and 4”Shorthand for at([4, 12], 16, opts).
clap.play(backbeat({ velocity: 0.9 }));euclidean — evenly-distributed hits
Section titled “euclidean — evenly-distributed hits”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 stepseuclidean(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 triadgenerate — compute each step
Section titled “generate — compute each step”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 — a melodic step grid
Section titled “notes — a melodic step grid”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).
Composing patterns
Section titled “Composing patterns”Every constructor returns a Pattern, and patterns compose with free-function
combinators — stack, 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.