Loading Patchies...

Clock API

The clock object provides beat-synced timing and scheduling for JavaScript-based objects. It reads from the global Transport and works identically across all environments (main thread and workers).

Supported Objects

The clock object is available in: js, worker, p5, canvas, three, textmode, hydra, and DOM variants.

Clock Properties

Property Type Description
clock.time number Current time in seconds
clock.ticks number Current time in ticks (192 PPQ)
clock.beat number Current beat in measure (0 to beatsPerBar-1)
clock.phase number Position within current beat (0.0 to 1.0)
clock.bpm number Current tempo in BPM
clock.bar number Current bar (0-indexed)
clock.beatsPerBar number Beats per bar (default: 4)
clock.timeSignature [number, number] Time signature: [6, 8] is 6/8

Basic Usage

// Use clock.time for animations
const x = Math.sin(clock.time) * 100;
const y = Math.cos(clock.time * 0.5) * 50;
circle(width/2 + x, height/2 + y, 20);

// Use clock.phase for beat-synced pulsing
const pulse = 1 + clock.phase * 0.5;
circle(width/2, height/2, 50 * pulse);

// Use clock.beat to change on each beat
const colors = ['red', 'blue', 'green', 'yellow'];
fill(colors[clock.beat]);

Control Methods

Control the transport directly from your code:

Method Description
clock.play() Start transport
clock.pause() Pause transport
clock.stop() Stop and reset to 0
clock.setBpm(bpm) Set tempo
clock.setTimeSignature(n, d) Set time signature (e.g., 6, 8 for 6/8)
clock.seek(seconds) Seek to time in seconds

Transport Control Example

// React to messages
recv((m) => {
  if (m === 'go') {
    clock.setBpm(140);
    clock.play();
  }

  if (m === 'drop') {
    clock.setBpm(70); // half-time feel
  }
});

// Auto-start on load
clock.play();

Time Signature Example

// Set 3/4 time (3 quarter-note beats per bar)
clock.setTimeSignature(3, 4);

// Set 6/8 time (6 eighth-note beats per bar)
clock.setTimeSignature(6, 8);

// Now clock.beat cycles 0, 1, 2, 0, 1, 2...
clock.onBeat(0, () => kick());   // downbeat of each bar
clock.onBeat(2, () => snare());  // beat 3 of each bar

Subdivision Methods

Subdivisions are computed per-node — different nodes can use different subdivisions simultaneously (e.g., one node with triplets, another with quintuplets).

Method Return Description
clock.subdiv(n) number Current subdivision index (0 to n-1) within the beat
clock.subdivPhase(n) number Progress within current subdivision (0.0 to 1.0)

Quintuplets Example

// Each node picks its own subdivision count — no global state
clock.subdiv(5);       // → 0, 1, 2, 3, 4 within each beat
clock.subdiv(3);       // → 0, 1, 2 (triplets, can run simultaneously)
clock.subdiv(4);       // → 0, 1, 2, 3 (sixteenths)

Polyrhythmic Patterns

// Node A uses triplets, Node B uses quintuplets — at the same time
const triAngle = clock.subdiv(3) / 3 * TAU;
const quintAngle = clock.subdiv(5) / 5 * TAU;

Smooth Animation with subdivPhase

// Pulse that breathes once per sixteenth note
const t = clock.subdivPhase(4);
const radius = 50 + 20 * Math.sin(t * Math.PI);
circle(width/2, height/2, radius);

Rhythmic Color Changes

const colors = ['red', 'orange', 'yellow', 'green', 'blue'];
fill(colors[clock.subdiv(5)]);

Scheduling Methods

Instead of manually tracking beat changes, use these scheduling methods for cleaner code. All scheduling callbacks receive a time argument — the precise transport time of the event.

By default, callbacks fire after the event — ideal for visuals. Pass { audio: true } as the last argument for lookahead scheduling, where callbacks fire ~100ms early with the precise time for Web Audio API scheduling.

onBeat

Subscribe to beat changes. Callback fires when the specified beat is reached.

// Fire on specific beat (0 to beatsPerBar-1)
clock.onBeat(0, () => kick());      // downbeat
clock.onBeat(2, () => snare());     // beat 3

// Fire on multiple beats
clock.onBeat([0, 2], () => snare()); // beats 1 and 3

// Fire on every beat
clock.onBeat('*', () => hihat());

// Audio-precise scheduling — fires early with precise time
clock.onBeat(0, (time) => {
  oscillator.start(time);
}, { audio: true });

schedule

Schedule a one-shot callback at a specific time.

The bar:beat:sixteenth notation is zero-indexed (like Tone.js) — '0:0:0' is the start, '1:0:0' is 1 bar from the start, and so on. This differs from DAWs like Ableton (which start at 1.1.1).

// Absolute time in seconds
clock.schedule(clock.time + 2, () => drop());

// Bar:beat:sixteenth notation (zero-indexed)
clock.schedule('4:0:0', () => breakdown());  // 4 bars from start
clock.schedule('8:2:0', () => buildUp());    // 8 bars + 2 beats from start

// Audio-precise — fires early with precise time
clock.schedule('4:0:0', (time) => {
  send({ type: 'set', value: 880, time });
}, { audio: true });

every

Schedule a repeating callback at a musical interval.

// Bar:beat:sixteenth interval
clock.every('1:0:0', () => flash());    // every bar
clock.every('0:1:0', () => pulse());    // every beat
clock.every('0:0:1', () => tick());     // every sixteenth

// Audio-precise repeating — fires early with grid-aligned time
clock.every('0:1:0', (time) => {
  send({
    type: 'trigger',
    values: { peak: 1, sustain: 0.7 },
    attack: 0.01,
    decay: 0.1,
    time
  });
}, { audio: true });

setTimelineStyle

All onBeat, schedule, and every callbacks are automatically visualized in the Timeline Viewer when it's open. Each node gets a unique color, and you'll see markers for upcoming events and brief flash animations when they fire.

Use setTimelineStyle to customize how this node appears in the Timeline Viewer.

// Set a custom color for this node in the timeline
clock.setTimelineStyle({ color: '#ff6b6b' });

// Hide this node from the timeline entirely
clock.setTimelineStyle({ visible: false });

// Show again with a specific color
clock.setTimelineStyle({ color: '#4af', visible: true });
Option Type Description
color string CSS color for this node's markers and label
visible boolean Whether this node appears in the timeline (default: true)

cancel

Cancel scheduled callbacks.

// Cancel specific callback
const id = clock.onBeat(0, () => flash());
clock.cancel(id);

// Cancel all callbacks (automatic on code change)
clock.cancelAll();

Examples

Beat Visualization

// Flash background on downbeat
clock.onBeat(0, () => {
  background(255);
  setTimeout(() => background(0), 100);
});

// Pulse circle on every beat
function draw() {
  const size = 100 + clock.phase * 50;
  circle(width / 2, height / 2, size);
}

Scheduled Transitions

// Build-up and drop
clock.schedule('4:0:0', () => setMode('intense'));
clock.schedule('8:0:0', () => setMode('drop'));

Repeating Patterns

// Color cycle every 4 bars
let colorIndex = 0;
const colors = ['red', 'blue', 'green', 'yellow'];

clock.every('4:0:0', () => {
  setColor(colors[colorIndex++ % colors.length]);
});

Manual Beat Detection (Alternative)

If you prefer manual control over scheduling:

let lastBeat = -1;

function draw() {
  if (clock.beat !== lastBeat) {
    // Beat changed!
    if (clock.beat === 0) flash();
    lastBeat = clock.beat;
  }
}

Seek to Bar

// Jump to bar 8
const secondsPerBar = (60 / clock.bpm) * clock.beatsPerBar;
clock.seek(8 * secondsPerBar);

Audio-Rate Beat Sync (beat~)

For continuous beat-synced signals in the audio graph, use the beat~ object. Unlike clock.schedule which fires discrete callbacks, beat~ outputs a sample-by-sample 0→1 sawtooth ramp synchronized to the transport BPM.

beat~       → ramp 0→1 once per beat
beat~ 4     → ramp 0→1 four times per beat (16th notes)
beat~ 0.25  → ramp 0→1 once per bar (in 4/4)

The multiply parameter scales the beat frequency: 1 = per beat (default), 2 = 8th notes, 4 = 16ths, 0.25 = per bar. It's an AudioParam, so other signals can modulate it.

The output follows transport play/pause/stop — it freezes when paused and resets on stop.

Tone.js Scheduling

If you're using Tone.js, you can also schedule events through the Tone.js Transport. This is useful when working with Tone.js synths and effects, since they integrate directly with Tone.getTransport().

// In a tone~ object — Tone.js Transport is synced to the global transport
const synth = new Tone.Synth().connect(outputNode);

Tone.getTransport().scheduleRepeat((time) => {
  synth.triggerAttackRelease('C4', '8n', time);
}, '4n');

Tone.getTransport().schedule((time) => {
  synth.triggerAttackRelease('E4', '2n', time);
}, '4:0:0');

Always use the time callback argument (not Tone.now()) for sample-accurate timing.

When to use which:

Approach Best for
clock.onBeat / every / schedule Visual sync — fires after event (~25ms precision)
Any method + { audio: true } Audio scheduling — fires early with precise time arg
beat~ Continuous audio-rate modulation (tremolo, FM, waveshaping)
tone~ + Tone.Transport Scheduling with Tone.js synths/effects

Scheduling Audio Parameters

Pass { audio: true } to use lookahead scheduling with parameter automation messages. Then, pass the time argument from the callback into the set, trigger and release messages — this is used as an absolute time by default.

This lets you automate audio parameters (gain~, osc~, filters, etc.) with beat-synced, sample-accurate timing:

// Trigger an envelope on every downbeat
clock.onBeat(0, (time) => {
  send({
    type: 'trigger',
    values: { peak: 1, sustain: 0.7 },
    attack: 0.01,
    decay: 0.1,
    time
  });
}, { audio: true });

// Schedule a filter sweep at bar 4
clock.schedule('4:0:0', (time) => {
  send({ type: 'set', value: 2000, time });
}, { audio: true });

// Repeating audio-precise scheduling every beat
clock.every('0:1:0', (time) => {
  send({
    type: 'trigger',
    values: { peak: 1, sustain: 0.7 },
    attack: 0.01,
    decay: 0.1,
    time
  });
}, { audio: true });

Precision

All scheduling methods (onBeat, schedule, every) poll every ~25ms.

Default (visual): Callbacks fire after the event has occurred — accurate to ~25ms, imperceptible for visual sync.

{ audio: true }: Callbacks fire before the event, within a ~100ms lookahead window. The time argument contains the precise transport time of the event.

In worker environments (worker, canvas), all scheduling uses frame-based polling (~16ms at 60fps).

For continuous audio-rate signals, use beat~. For Tone.js integration, use tone~.

Hydra

In Hydra, you can use either clock.time or the bare time variable:

// Both work in Hydra
osc(10, 0.1, () => clock.time)
osc(10, 0.1, () => time)  // shorthand

See Also