The Role of Tone.Event in Tone.js Scheduling

This article explores the role of Tone.Event in Tone.js, a powerful class designed for scheduling general-purpose events along the audio timeline. You will learn how Tone.Event works, how it differs from other scheduling utilities, and how to implement it to trigger custom callback functions precisely in sync with the Tone.js Transport.

What is Tone.Event?

In Tone.js, Tone.Event is a flexible, low-level class used to schedule a callback function at a specific time along the Transport timeline. While classes like Tone.Sequence are tailored for step-sequencing and Tone.Part is built for multi-note musical scores, Tone.Event is the foundational tool for general-purpose, time-aligned actions.

It allows developers to trigger non-audio events—such as UI animations, state changes, or custom JavaScript logic—precisely synchronized with audio playback.

Key Features of Tone.Event

Tone.Event provides several features that make it superior to basic timeout functions or raw Transport scheduling:

How to Use Tone.Event

To use Tone.Event, you instantiate the class with a callback function and then schedule its start time. The callback function receives two arguments: the precise audio time at which the event occurs, and an optional value payload.

Here is a basic implementation:

import * as Tone from "tone";

// Create a synth to play a sound
const synth = new Tone.Synth().toDestination();

// Define the event
const myEvent = new Tone.Event((time, pitch) => {
    // Use the precise audio time to trigger the synth
    synth.triggerAttackRelease(pitch, "8n", time);
    console.log(`Triggered ${pitch} at transport time: ${time}`);
}, "C4");

// Schedule the event to start at the beginning of the Transport timeline
myEvent.start(0);

// Start the Transport to hear the event
Tone.Transport.start();

Creating Looping Events

Tone.Event is highly effective for creating repeating patterns that are more complex than standard metronomes but simpler than full musical parts.

const loopingEvent = new Tone.Event((time) => {
    // Your recurring action here
    console.log("Loop event fired at", time);
});

// Configure the loop settings
loopingEvent.loop = true;
loopingEvent.loopEnd = "1m"; // Loop every 1 measure
loopingEvent.playbackRate = 1;

// Start looping at the 2nd measure
loopingEvent.start("2:0:0");

Tone.Event vs. Transport.schedule

While you can schedule callbacks directly using Tone.Transport.schedule(), using Tone.Event offers distinct advantages:

  1. Encapsulation: Tone.Event wraps the event logic, timing, and state into a single object.
  2. Dynamic Manipulation: With Transport.schedule(), once an event is scheduled, you must use an ID to cancel it. With Tone.Event, you can call .stop(), .start(), or mutate properties like .playbackRate directly on the object instance at any time during runtime.