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

aleatoric

v0.1.0-beta.0

Published

APIs for the creation of aleatoric music — chance operations, stochastic processes, and indeterminate composition

Readme

aleatoric

Core TypeScript library for algorithmic and chance-based music composition — pitches, scales, chords, probability distributions, and generators for Markov chains, cellular automata, dice music, and more.

npm version license

Features

  • Core music primitives — pitches, scales, chords, intervals, rhythms
  • Chance engine — seeded PRNG, probability distributions, dice, coin flips, I Ching
  • Generators — random pitch/rhythm, Markov chains, cellular automata (Game of Life), dice music (Musikalisches Würfelspiel), chance ops, Perlin-style noise melodies
  • Constraints — post-processing pipeline to shape generated sequences
  • Timeline — sorted event collection with merge, slice, quantize, and offset
  • MIDI — runtime-agnostic MIDI player and message helpers for sending events to hardware synths and DAWs
  • Zero runtime dependencies
  • Dual CJS + ESM build — tree-shakeable, works in Node.js and the browser

Install

Current npm releases are prereleases (WIP). Install the beta dist-tag or pin a version:

npm install aleatoric@beta
# or
npm install [email protected]

In package.json: "aleatoric": "0.1.0-beta.0" or "aleatoric": "^0.1.0-beta.0". Please include the installed version when reporting issues.

When a stable latest line is published, npm install aleatoric will track that tag.

Quick Start

Core primitives

import { Scale, Chord, pitchToMidi, midiToFrequency } from 'aleatoric';

const scale = Scale.pentatonic('C');
const pitches = scale.getPitches(4); // C4, D4, E4, G4, A4

const chord = Chord.create('C', 'major7');
const notes = chord.getPitches(4); // C4, E4, G4, B4

const midi = pitchToMidi({ name: 'A', octave: 4 }); // 69
const freq = midiToFrequency(midi);                  // 440.0 Hz

Chance engine

import { SeededRng, flipCoin, rollDie, castHexagram, hexagramToRange, gaussian } from 'aleatoric';

const rng = new SeededRng(42); // reproducible

flipCoin(0.7, rng);            // 'heads' or 'tails' with 70% bias
rollDie(6, rng);               // 1–6

const hexagram = castHexagram(rng);    // I Ching hexagram (1–64)
const value = hexagramToRange(hexagram, 0, 127); // map to MIDI

const pitch = gaussian(60, 10, rng);   // normally distributed around middle C

Generators

import {
  generateRandomPitches,
  generateRandomRhythm,
  generateMarkovSequence,
  buildMidiTransitionMatrix,
  generateCellularAutomata,
  generateChanceOps,
  Scale,
  SeededRng,
} from 'aleatoric';

const rng = new SeededRng(1);
const scale = Scale.major('D');

// Random pitches constrained to a scale
const pitchEvents = generateRandomPitches({
  count: 16,
  scale,
  low: 48,
  high: 84,
  distribution: 'gaussian',
  rng,
});

// Random rhythm
const rhythmEvents = generateRandomRhythm({ count: 8, restProbability: 0.15, rng });

// First-order Markov chain
const matrix = buildMidiTransitionMatrix([60, 62, 64, 67, 64, 62, 60], 1);
const markovEvents = generateMarkovSequence({
  transitionMatrix: matrix,
  count: 32,
  rng,
});

// 2D cellular automaton (Conway's Game of Life)
const caEvents = generateCellularAutomata({
  steps: 16,
  width: 16,
  rng,
});

// Cage-style chance operations
const chanceEvents = generateChanceOps({
  count: 12,
  pitchMethod: 'iching',
  durationMethod: 'coin',
  rng,
});

Constraints

import {
  applyConstraints,
  ScaleConstraint,
  MaxLeapConstraint,
  RangeConstraint,
  ContourConstraint,
} from 'aleatoric';

const constrained = applyConstraints(pitchEvents, [
  new ScaleConstraint(Scale.minor('A')),
  new MaxLeapConstraint(7),          // no leaps larger than a perfect 5th
  new RangeConstraint(48, 84),       // stay within 4 octaves
  new ContourConstraint('arch'),     // shape phrase as a rise then fall
]);

Musikalisches Würfelspiel (dice music)

import { createSimpleDiceTable, generateDiceMusic, SeededRng } from 'aleatoric';

const table = createSimpleDiceTable({
  2:  [[60, 62, 64, 65]],
  3:  [[67, 65, 64, 62]],
  // ... entries for 4–12
  12: [[72, 71, 69, 67]],
}, /* duration per note */ 1, /* velocity */ 80);

const events = generateDiceMusic({ measures: 8, table, rng: new SeededRng(99) });

MIDI output

The MidiPlayer sends MusicEvents as MIDI messages through any MidiOutput implementation. Bring your own transport — Web MIDI API in the browser, easymidi in Node.js, or any custom backend.

import { MidiPlayer, Timeline, type MidiOutput } from 'aleatoric';

// Example: Web MIDI API in the browser
const access = await navigator.requestMIDIAccess();
const port = access.outputs.values().next().value;
const output: MidiOutput = {
  name: port.name ?? 'MIDI Out',
  send: (data, timestamp) => port.send(data, timestamp),
};

const player = new MidiPlayer(output, new Timeline(events), { bpm: 120, loop: true });
player.play();
// Example: Node.js with easymidi
import easymidi from 'easymidi';
import { MidiPlayer, Timeline, type MidiOutput } from 'aleatoric';

const port = new easymidi.Output('Virtual Port', true);
const output: MidiOutput = {
  name: 'easymidi',
  send: (data) => { /* decode status byte and call port.send() */ },
};

const player = new MidiPlayer(output, new Timeline(events), {
  bpm: 120, loop: true, deferSend: true,
});
player.play();

Web Audio / third-party synthesizers

Use SynthesisAdapter to plug in Tone.js, custom AudioWorklets, or any library that can start/stop notes at absolute audio-clock times (same idea as AudioContext.currentTime). SynthesisScheduler walks a Timeline with the same lookahead pattern as MidiPlayer, but calls adapter.schedule() instead of sending MIDI bytes.

import {
  SynthesisScheduler,
  Timeline,
  type ScheduledSynthesisNote,
  type SynthesisAdapter,
} from 'aleatoric';

const adapter: SynthesisAdapter = {
  schedule(note: ScheduledSynthesisNote) {
    // Map note.startTimeSec, note.durationSec, note.midi / note.frequencyHz
    // to your synth API (e.g. triggerAttackRelease).
  },
  cancelAll(atTimeSec) {
    // Optional: silence everything at atTimeSec (e.g. on stop).
  },
};

const ctx = new AudioContext();
const scheduler = new SynthesisScheduler(adapter, new Timeline(events), {
  getAudioTime: () => ctx.currentTime,
  bpm: 120,
  loop: false,
});

await ctx.resume();
scheduler.play();

Each ScheduledSynthesisNote includes the original MusicEvent as source if you need pitch or other fields beyond MIDI and frequency.

API Overview

core/

| Export | Description | |---|---| | Scale | 16 built-in scale types (major, minor, pentatonic, blues, dorian, aeolian, etc.) | | Chord | 14 chord qualities (triads, 7ths, 9ths, sus). getPitches(), inversion() | | Interval | Enum from Unison to Octave. intervalName, invertInterval, isConsonant | | pitchToMidi / midiToPitch | Convert between Pitch objects and MIDI note numbers | | midiToFrequency / pitchToFrequency | Frequency conversion | | parsePitch | Parse "C#4"Pitch | | DURATIONS | Standard note values in beats (quarter, eighth, half, etc.) | | effectiveDuration | Apply dot/triplet modifiers to a duration |

random/

| Export | Description | |---|---| | SeededRng | xoshiro128** PRNG — deterministic, forkable | | DefaultRng | Wraps Math.random | | weightedChoice / uniformChoice / shuffle | Weighted and uniform selection | | uniform / gaussian / exponential / poisson / triangular | Probability distributions | | rollDie / rollDice / roll2d6 | Dice rolling | | flipCoin / flipBool / coinChoice | Coin flips with optional bias | | castHexagram / hexagramToRange / hexagramSelect | I Ching (three-coin method, King Wen sequence) |

generators/

| Export | Description | |---|---| | generateRandomPitches | Uniform, gaussian, or edge-biased pitch generation | | generateRandomRhythm | Random duration selection with rest probability and density mode | | generateMarkovSequence | Nth-order Markov chains from a transition matrix | | generateDiceMusic | Musikalisches Würfelspiel — compose by rolling dice | | generateChanceOps | Cage-style: each parameter independently controlled by coin/I Ching/random | | generateCellularAutomata | Conway's Game of Life 2D grid mapped to pitch sequences | | generatePerlinNoiseMelody | Smooth pitch contours by interpolating noise-like waypoints through an optional scale |

generators/constrained

| Export | Description | |---|---| | applyConstraints | Apply an ordered list of constraints to a sequence of events | | ScaleConstraint | Snap pitches to the nearest scale degree | | MaxLeapConstraint | Limit maximum melodic leap in semitones | | RangeConstraint | Wrap pitches that exceed a MIDI range | | NoParallelFifthsConstraint | Avoid parallel perfect fifths between two voices | | ContourConstraint | Shape phrase contour: ascending, descending, arch, valley |

midi/

| Export | Description | |---|---| | MidiPlayer | Runtime-agnostic lookahead MIDI scheduler | | MidiOutput | Interface for MIDI output ports — implement with Web MIDI API, easymidi, or any transport | | noteOn / noteOff | Encode MIDI note-on and note-off messages as byte arrays | | controlChange / allNotesOff | Encode CC and all-notes-off messages | | MidiChannel / MidiPlayerOptions | Supporting types |

scheduler/

| Export | Description | |---|---| | Timeline | Ordered collection of MusicEvents. Supports merge, slice, quantize, offset |

playback/

| Export | Description | |---|---| | SynthesisAdapter | Implement schedule(note) (and optionally cancelAll) for a third-party Web Audio synth | | ScheduledSynthesisNote | Start time, duration, MIDI, velocity, Hz, plus source MusicEvent | | SynthesisScheduler | Lookahead playback from a Timeline using getAudioTime() (e.g. AudioContext.currentTime) | | SynthesisSchedulerOptions | bpm, lookahead, interval, loop, getAudioTime |

Further reading

Background on the pseudorandom, statistical, and generative ideas used in this library (third-party sites; URLs may change over time).

PRNG and seeding

  • xoshiro128** / xoshiro family — Sebastiano Vigna and David Blackman's small fast PRNGs (includes reference code and papers).
  • SplitMix-style mixing — common way to expand a single seed into a full PRNG state (see also the SplitMix notes on Vigna's site).

Distributions and sampling

I Ching

Generators and musical context

Reproducibility

All generators accept an optional rng parameter. Pass a SeededRng with a fixed seed to produce the same output on every run:

import { SeededRng, generateRandomPitches } from 'aleatoric';

const events = generateRandomPitches({ count: 8, rng: new SeededRng(1234) });
// Always produces the same sequence

Interactive docs

Run npm run docs:dev from the repo root for the interactive docs app (packages/docs).

npm run docs:dev
npm run docs:build -w aleatoric-docs

See Contributing for all docs commands.

License

MIT © Collin Klippel