npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@resequence/patterns

v0.1.1

Published

Temporal composition engine for building hierarchical trees of musical events. Patterns describe arrangement — what to play and when — and export to MIDI.

Downloads

208

Readme

@resequence/patterns

Temporal composition engine for building hierarchical trees of musical events. Patterns describe arrangement — what to play and when — and export to MIDI.

npm install @resequence/patterns

Overview

Build pattern trees from notes, sequences, layers, repeats, and modifiers. The tree produces a flat stream of typed events — notes, metadata, tempo changes — that downstream systems interpret. MIDI export is built in.

import { repeat, echo, transpose, distribute } from "@resequence/patterns";
import { N, sequence, layer, gap, tempo } from "@resequence/patterns/core";
import { renderMidi } from "@resequence/patterns/utils";

const kick = repeat(N.C2(1), 4).route("drums");
const snare = sequence(gap(1), N.D3(1), gap(1), N.D3(1)).route("drums");

const bassline = sequence(N.C2(2), N.Ds2(1), N.F2(1), N.G2(2), N.As2(1), N.G2(1)).route("bass");

const chords = layer(
	sequence(N.C4(4), N.Gs3(4)),
	sequence(N.Ds4(4), N.As3(4)),
	sequence(N.G4(4), N.Ds4(4)),
).route("pad");

const hats = echo(distribute(3, 8, N.C2.pitch, 0.5), 2, 0.25, 0.6).route("drums");

const song = tempo(
	layer(repeat(layer(kick, snare, hats), 4), repeat(bassline, 2), repeat(chords, 2)),
	128,
);

const midiFiles = renderMidi(song);
// Map<string, Uint8Array> — one MIDI file per port ("drums", "bass", "pad")

Patterns are mutable trees. Notes carry Pitch objects and beat-based durations. Modifiers clone their input and transform the clone's notes at construction time, preserving the caller's original. Everything composes — patterns into larger patterns, modifiers on top of modifiers.

Package Structure

This package provides modifiers, generators, and the CLI. It re-exports the protocol and utility layers via subpath exports:

  • @resequence/patterns — modifiers + generators
  • @resequence/patterns/core — protocol types, base classes, containers, pitch, tuning (re-export of @resequence/patterns-core)
  • @resequence/patterns/utils — MIDI rendering, validation, built-in tunings (re-export of @resequence/patterns-utils)

These are also available through the resequence bridge package as resequence/patterns, resequence/patterns/core, and resequence/patterns/utils. Most users should install resequence and use the bridge subpaths. Depend on @resequence/patterns-core directly for lighter protocol-only usage (e.g., writing custom modifiers without depending on the full library).

Pitch

The N namespace provides typed pitch access for the chromatic scale (A4 = 440 Hz). Sharps use s, flats use b. Each entry is callable — pass a duration in beats to create a note:

import { N } from "@resequence/patterns/core";

N.C4(1)         // quarter note middle C
N.Cs4(0.5)      // eighth note C#4
N.Db4(2, 80)    // half note Db4 at velocity 80
N.Fs3(1)        // quarter note F#3

Access the Pitch object via the .pitch property when you need the pitch without creating a note:

N.C4.pitch      // Pitch { class: 0, octave: 4, stepsPerOctave: 12, octaveRatio: 2 }

Chords and Scales

CD builds chords from a quality string and duration:

import { CD, CS } from "@resequence/patterns/utils";

CD("Cm7", 2);       // Cm7 chord, half note each
CD("Gmaj9", 4);     // Gmaj9 chord, whole note

CS creates a scale accessor — call it with degree and octave to get notes:

const scale = CS("C4 minor");
scale(0);    // C4
scale(2);    // Eb4

Custom Tunings

createTuning produces a namespace like N for any tuning system:

import { createTuning } from "@resequence/patterns/core";

// Equal temperament
const quarterTone = createTuning({
	classes: ["C", "C+", "Cs", "Cs+", "D", /* ... 24 classes */],
	reference: { class: "A", octave: 4, hz: 440 },
});

quarterTone.C4(1);
quarterTone["C+4"](1);    // quarter-tone between C and C#

// Frequency-based (just intonation, custom scales)
const justC = createTuning({
	frequencies: { C: 261.63, D: 294.33, E: 327.03, /* ... */ },
	referenceOctave: 4,
});

Built-in tunings: Chromatic and N are in @resequence/patterns/core. All others — Midi, A432, QuarterTone, Just, Pythagorean, Meantone, Werckmeister, Pelog, Slendro, BohlenPierce, Tet19, Tet31, Tet53 — are in @resequence/patterns/utils.

Patterns

Leaves

note(pitch, duration, options?) — a single note. Most of the time you'll use the N shorthand instead.

gap(duration) — silence with a duration. Used as a spacer in sequences.

Containers

sequence(...patterns) — serial placement. Children play one after another. Duration is the sum.

layer(...patterns) — parallel placement. Children play simultaneously. Duration is the max.

repeat(pattern, count) — loop a pattern. Duration is pattern.duration * count.

import { repeat } from "@resequence/patterns";
import { N, sequence, layer, gap } from "@resequence/patterns/core";

// 4-bar drum loop
const drums = repeat(
	layer(
		repeat(N.C2(1), 4),                              // kick on every beat
		sequence(gap(1), N.D3(1), gap(1), N.D3(1)),      // snare on 2 and 4
	),
	4,
);

Containers accept Pattern | Note, so you can pass raw notes directly: sequence(N.C4(1), N.E4(1), N.G4(2)).

Modifiers

Modifiers wrap a pattern, transform its notes at construction time, and persist as tree nodes. The original pattern is preserved in the modifier's input property.

Pitch

transpose(pattern, interval) — shift all notes by N steps.

harmonize(pattern, intervals) — duplicate notes at multiple intervals. harmonize(melody, [0, 4, 7]) creates a major triad from each note.

invert(pattern, position) — chord inversion. invert(chord, 1) moves the lowest note up an octave.

voicing(pattern, style) — rearrange chord voicing. Styles: "spread", "drop2", "drop3", "firstInversion", "secondInversion", etc.

flip(pattern) — mirror pitches around the midpoint.

unison(pattern, spread?) — duplicate notes with slight pitch offsets. unison(melody, { count: 3, interval: 0.1 }).

slide(pattern, options?) — add pitch slides between consecutive notes. Options: duration, curve, absolute.

Rhythm

quantize(pattern, grid?, swing?, humanize?, seed?) — snap notes to a grid with optional swing and humanization.

shuffle(pattern, amount?, seed?) — shuffle note timing.

stretch(pattern, factor) — scale all durations by a factor.

length(pattern, duration) — set all note durations to a fixed value.

legato(pattern) — extend each note to meet the next.

offset(pattern, beats) — shift all notes forward or backward in time.

jitter(pattern, amount?, seed?) — randomize note timing with deterministic seed.

Articulation

strum(pattern, options?) — stagger simultaneous notes like a guitar strum. Options: direction ("up" / "down"), interval, offset, seed.

flam(pattern) — quick grace note before each note.

echo(pattern, count?, delay?, decay?, spread?) — create delayed repetitions with decaying velocity.

chop(pattern, stepSize) — slice notes into repeated steps of fixed size.

gate(pattern, ratio?) — shorten notes to a fraction of their duration.

accent(pattern, positions, amount) — boost velocity at specific beat positions.

Arp

arp(pattern, mapping) — arpeggiate using a custom rhythm pattern.

arpUp(pattern, octaves?) — arpeggiate ascending.

arpDown(pattern, octaves?) — arpeggiate descending.

arpUpDown(pattern, octaves?) — arpeggiate up then down.

arpRandom(pattern, seed?) — arpeggiate in random order with deterministic seed.

Structure

slice(pattern, from, to) — extract a beat range.

trim(pattern, from, to) — remove beats from the start and/or end.

splice(pattern, index, deleteCount, ...items) — remove and insert notes by index.

insert(pattern, index, ...items) — insert notes at an index.

reverse(pattern) — reverse note order.

rotate(pattern, steps) — rotate notes by N positions.

sort(pattern, key?) — sort notes by "pitch", "velocity", or "beat".

Dynamics

level(pattern, config) — adjust velocity and CC values. Supports absolute values, randomization, expansion, and compression.

humanize(pattern, config) — alias for level. Apply randomized velocity variation.

polyphony(pattern, voices?) — limit simultaneous notes.

Functional

map(pattern, fn) — transform each note, returning a replacement.

flatMap(pattern, fn) — transform each note into zero or more notes.

filterNotes(pattern, fn) — remove notes that don't match a predicate.

thin(pattern, probability, seed?) — randomly remove notes.

mask(pattern, maskPattern) — keep only notes that overlap with the mask pattern's timing.

heal(pattern) — close gaps between notes by extending durations.

Non-Destructive

mute(pattern) — silence without changing duration. The inner pattern remains readable for visualization.

skip(pattern) — remove from the timeline entirely. Zero duration.

These are in @resequence/patterns/core.

Generators

Generators produce notes procedurally. They extend MidiPattern — routing, modifiers, and containers all work on them.

distribute(hits, steps, pitch, stepDuration, rotate?) — Euclidean rhythm. Places hits evenly across steps. The classic Bjorklund algorithm for world rhythm patterns.

import { distribute } from "@resequence/patterns";
import { N } from "@resequence/patterns/core";

distribute(3, 8, N.C2.pitch, 0.5);        // tresillo
distribute(5, 16, N.C2.pitch, 0.25, 2);   // rotated 16th pattern

random(options) — random notes with configurable pitch set, velocity range, count, step duration, and deterministic seed.

random({ pitches: [N.C4.pitch, N.E4.pitch, N.G4.pitch], count: 16, stepDuration: 0.5, seed: 42 });

generative(callback, count, duration?) — procedural generation with an accumulator. The callback receives all previously generated notes, enabling interdependent relationships.

import { generative } from "@resequence/patterns";
import { note, N } from "@resequence/patterns/core";

generative((acc, i) => [note(N.C4.pitch, 1)], 8);

Routing

Patterns connect to named output ports via .route(portName, channel?). Port names accumulate through the tree — children inherit parent ports. Notes dispatch to every port in scope.

import { N, sequence, layer } from "@resequence/patterns/core";

const melody = sequence(N.C4(1), N.E4(1), N.G4(2)).route("lead");
const bass = sequence(N.C2(2), N.G2(2)).route("bass");

layer(melody, bass); // "lead" and "bass" ports produce separate note streams

Notes without a route produce no events during scheduling.

Metadata

Wrap any pattern with metadata for tempo, time signature, tuning, markers, and regions:

import { metadata, tempo, timeSignature, marker, cue, region, tuning } from "@resequence/patterns/core";
import { Just } from "@resequence/patterns/utils";

// Full metadata object
metadata(pattern, { tempo: 140, timeSignature: [3, 4] });

// Individual helpers — each wraps a pattern
tempo(pattern, 128);
tempo(pattern, 120, { keyframes: [{ beat: 0, value: 0.5 }, { beat: 16, value: 1.0 }] });
timeSignature(pattern, [3, 4]);
marker(pattern, "chorus");
cue(pattern, "drop");        // alias for marker
region(pattern, "verse");
tuning(pattern, Just.config);

Tempo supports keyframe automation for gradual BPM changes. Values are normalized 0–1, mapped to the range [1, 999] BPM.

MIDI Export

import { renderMidi } from "@resequence/patterns/utils";

const midiFiles = renderMidi(song, { ppq: 480 });
// Map<string, Uint8Array> — one Standard MIDI File per port

The renderer handles pitch bend allocation across MIDI channels (avoiding conflicts with simultaneous bends), CC events, lyric meta events, tempo changes, time signatures, and markers.

CLI

npx resequence-patterns export --input ./song.ts --output ./midi --ppq 480

The entry file must export a Pattern as its default export. One .mid file is written per port.

Custom Patterns

Custom Modifier

Modifiers wrap a pattern and transform its notes at construction time. Extend ModifierPattern, define a properties interface, and use modifyNotes() or flatModifyNotes() for the transformation. The factory clones the input before mutating.

import {
	ModifierPattern, ModifierProperties, PatternProperties, Pattern, Note,
	toPattern, modifyNotes,
} from "@resequence/patterns/core";

interface DoubleTimeProperties extends ModifierProperties {
	readonly factor: number;
}

class DoubleTimePattern extends ModifierPattern<DoubleTimeProperties> {
	readonly type = ["pattern", "midi", "modifier", "doubleTime"] as const;

	clone(overrides: Partial<PatternProperties>): DoubleTimePattern {
		return new DoubleTimePattern({ ...this.properties, ...overrides });
	}
}

export function doubleTime(pattern: Pattern | Note, factor = 2): DoubleTimePattern {
	const resolved = toPattern(pattern);
	const cloned = resolved.clone({});

	modifyNotes(cloned, (note) => ({
		...note,
		startBeat: note.startBeat / factor,
		endBeat: note.endBeat / factor,
	}));

	return new DoubleTimePattern({
		notes: cloned.notes,
		children: cloned.children,
		startBeat: cloned.startBeat,
		endBeat: cloned.endBeat,
		input: resolved,
		factor,
	});
}

The factory function does the work — the class is a thin wrapper storing parameters and implementing clone(). The input field preserves the original unmodified pattern.

Custom Generator

Generators extend MidiPattern and compute notes in their constructor. They produce notes from scratch rather than transforming existing ones.

import { MidiPattern, MidiPatternProperties, PatternProperties, RelativeNote, note } from "@resequence/patterns/core";

interface WalkProperties extends MidiPatternProperties {
	readonly startPitch: number;
	readonly steps: number;
}

class WalkPattern extends MidiPattern<WalkProperties> {
	readonly type = ["pattern", "midi", "generator", "walk"] as const;

	clone(overrides: Partial<PatternProperties>): WalkPattern {
		return new WalkPattern({ ...this.properties, ...overrides });
	}
}

export function walk(startPitch: number, steps: number): WalkPattern {
	const notes: RelativeNote[] = [];
	let pitch = startPitch;

	for (let i = 0; i < steps; i++) {
		notes.push({ offsetBeats: i, note: note({ class: pitch % 12, octave: Math.floor(pitch / 12), stepsPerOctave: 12, octaveRatio: 2 }, 1) });
		pitch += Math.random() > 0.5 ? 1 : -1;
	}

	return new WalkPattern({ notes, children: [], startBeat: 0, endBeat: steps, startPitch, steps });
}

Custom Event Types

Patterns can emit arbitrary events via _setup(), _schedule(), and _teardown(). Events are discriminated on type and bubble up through the tree unchanged — downstream consumers interpret them.

import { Pattern, PatternProperties, Event } from "@resequence/patterns/core";

interface LightEvent {
	readonly type: "light";
	readonly color: string;
	readonly beat: number;
}

function isLightEvent(event: Event): event is LightEvent {
	return event.type === "light";
}

class LightCuePattern extends Pattern {
	readonly type = ["pattern", "lightCue"] as const;

	override _schedule(absoluteBeat: number): Event[] {
		return [{ type: "light", color: this.properties.color, beat: absoluteBeat }];
	}

	clone(overrides: Partial<PatternProperties>): LightCuePattern {
		return new LightCuePattern({ ...this.properties, ...overrides });
	}
}

Tree Utilities

Walk and modify pattern trees:

import { walkPatterns, walkNotes, collectNotes, modifyNotes, flatModifyNotes, modifyPatterns } from "@resequence/patterns/core";

// Visit every pattern node
walkPatterns(song, (pattern, absoluteBeat) => { /* ... */ });

// Visit every note with a unique ID and absolute timing
walkNotes(song, (note, context) => {
	console.log(context.id, context.absoluteOffset);
});

// Collect all notes
const notes = collectNotes(song);

// Modify notes in place
modifyNotes(song, (note) => ({ ...note, velocity: Math.min(127, note.velocity + 20) }));

Validation

import { validate } from "@resequence/patterns/utils";

const warnings = validate(song);
// Reports orphaned notes — notes without port routing in their ancestor chain