dead-on
TypeScript icon, indicating that this package has built-in type declarations

2.2.1 • Public • Published


DeadOn

DeadOn is a drift-less, performance.now()–based clock for precision musical timing in web applications. It emits millisecond-accurate ticks at a configurable PPQN resolution, making it ideal for synchronising Web Audio, Web MIDI, UI updates, or other precise scheduling needs.


Table of Contents


Why DeadOn?

  • Drift-less scheduling: Uses performance.now() for a reliable timebase.
  • Configurable resolution: Adjustable PPQN for any musical subdivision.
  • Tunable lookahead & interval: Balance scheduling ahead of time with responsiveness.
  • Lightweight: No dependencies and minimal setup.

Installation

Install via npm:

npm install dead-on

Basic Usage

Creating a Clock

import { DeadOnClock } from "dead-on";

const audioCtx = new AudioContext(); // optional for sample-accurate audioTime

const clock = new DeadOnClock({
  bpm: 120, // beats per minute (default: 120)
  ppqn: 24, // pulses per quarter-note (default: 24)
  lookahead: 50, // how far ahead to schedule in ms (default: 50)
  interval: 20, // main loop interval in ms (default: 20)
  audioContext: audioCtx, // optional AudioContext
});

clock.start();

Subscribing to Ticks

const quarterTicks = clock.ppqn;
const barTicks = clock.ppqn * 4;

clock.on("tick", (e) => {
  // e.timeMs: high-resolution wall-clock timestamp (performance.now())
  // e.audioTime: AudioContext.currentTime if provided, else timeMs/1000
  // e.tick: integer tick count

  if (e.tick % quarterTicks === 0) {
    // quarter-note event
  }
  if (e.tick % barTicks === 0) {
    // bar event
  }
});

Scheduling One-Off Callbacks

Use clock.scheduleAt to run a callback exactly at a future timeMs:

clock.scheduleAt(() => {
  console.log("Runs precisely on the tick!");
}, e.timeMs);

API

new DeadOnClock(opts)

Creates a new clock.

Option Type Default Description
bpm number 120 Beats per minute
ppqn number 24 Pulses per quarter-note
lookahead number (ms) 50 How far ahead to schedule events
interval number (ms) 20 Main loop interval
audioContext AudioContext - Optional for sample-accurate audioTime

Methods

  • clock.start()void
    Start the clock (no-op if already running).

  • clock.stop()void
    Stop the clock.

  • clock.setBpm(bpm: number)void
    Change the tempo on the fly.

  • clock.setPpqn(ppqn: number)void
    Change the resolution on the fly.

  • clock.on('tick', callback)void
    Subscribe to tick events.
    callback(e: { timeMs: number; audioTime: number; tick: number })

  • clock.off('tick', callback)void
    Unsubscribe from tick events.

  • clock.scheduleAt(callback: () => void, timeMs: number)void
    Schedule a one-off callback at a specific performance.now() timestamp.


DeadOnSequencer

A fixed-length, latency-free step sequencer built on top of DeadOnClock.

import { DeadOnSequencer, StepAction } from "dead-on";

// Create a 16-step sequencer
const seq = new DeadOnSequencer(clock, 16);

// Define a pattern
type Note = { freq: number; durationMs: number };
const pattern: Array<StepAction<Note> | null> = Array(16).fill(null);
pattern[0] = {
  payload: [{ freq: 440, durationMs: 200 }],
  subdivs: 0, // play all payloads at once
  offsetMs: 10, // shift by 10ms
};

seq.setSequence(pattern);
// Adjust playback speed (e.g. half-speed)
seq.setSpeedFactor(0.5);

// Play payloads on each tick
clock.on("tick", (e) => {
  const payloads = seq.getPayloadsForTick(e.tick);
  for (const note of payloads) {
    const osc = audioCtx.createOscillator();
    DeadOnSequencer.triggerAudio(osc, e.audioTime, note.durationMs);
  }
});

StepAction

interface StepAction<P> {
  payload: P[]; // one or more items to schedule
  subdivs?: number; // subdivisions per step (default: 0)
  offsetMs?: number; // humanization or timing offset in ms
}

Methods

Method Description
new DeadOnSequencer(clock, steps) Create a sequencer with given number of steps
seq.setSequence(seq) Replace entire sequence at once
seq.setStep(step, action) Set or clear a single step
seq.clearSequence() Clear all steps
seq.clearStep(step) Clear a specific step
seq.setBpm(bpm) Change sequencer tempo
seq.setPpqn(ppqn) Change sequencer resolution
seq.setSpeedFactor(factor) Change playback speed by a continuous factor (1 = normal; <1 = slower; >1 = faster)
seq.getPayloadsForTick(tick) Get payloads scheduled on a given tick

Static Helpers

  • DeadOnSequencer.triggerAudio(osc: OscillatorNode, startTimeSec: number, durationMs?: number)
  • DeadOnSequencer.triggerMidi(midiOut: MIDIOutput, note: number, velocity: number, startTimeMs: number, offTimeMs?: number)

License

Apache 2.0 © 2xAA

Package Sidebar

Install

npm i dead-on

Weekly Downloads

18

Version

2.2.1

License

MIT

Unpacked Size

41.9 kB

Total Files

13

Last publish

Collaborators

  • 2xaa