Update Tone.Sequence Grid Dynamically in Tone.js

This article explains how to dynamically alter the subdivision, grid, and playback speed of a Tone.Sequence in Tone.js during runtime. You will learn how to directly modify the subdivision property, utilize the playback rate for relative timing adjustments, and update the events array to keep your synthesizers and samplers perfectly in sync with changing musical rhythms.

In Tone.js, a Tone.Sequence stepped loop is driven by a specific subdivision (such as "4n", "8n", or "16n"). Adjusting this grid dynamically allows you to create build-ups, tempo-halving effects, or complex generative rhythms without stopping the Tone.Transport.

There are two primary methods to dynamically update the grid of an active sequence.


Method 1: Directly Updating the subdivision Property

The most straightforward way to change the grid of a Tone.Sequence is by directly reassigning its subdivision property. Tone.js handles this update smoothly in real-time.

// Initialize a sequence with eighth notes
const seq = new Tone.Sequence((time, note) => {
    synth.triggerAttackRelease(note, "0.1", time);
}, ["C4", "E4", "G4", "B4"], "8n").start(0);

// Dynamically change the grid to sixteenth notes
function speedUpGrid() {
    seq.subdivision = "16n";
}

// Dynamically change the grid to quarter notes
function slowDownGrid() {
    seq.subdivision = "4n";
}

When you update the .subdivision property, Tone.js recalculates the interval between each step of the sequence on the next tick, maintaining the overall timing of your application.


Method 2: Adjusting the playbackRate

If you want to speed up or slow down the grid relatively without hardcoding a new subdivision string, you can modify the playbackRate property. This property acts as a multiplier for the sequence’s progress.

// A playbackRate of 1.0 is the default speed
seq.playbackRate = 1.0; 

// Double the speed of the sequence (e.g., "8n" plays at the speed of "16n")
seq.playbackRate = 2.0;

// Halve the speed of the sequence (e.g., "8n" plays at the speed of "4n")
seq.playbackRate = 0.5;

Using playbackRate is ideal for gradual accelerandos, ritardandos, or when you want to shift the grid relative to the master tempo using a slider control.


Updating the Events Array alongside the Grid

When changing subdivisions, the length of your sequence may no longer match the time signature or loop length you desire. You can dynamically update the .events array at the same time to match the new grid size.

// Change grid to 16th notes and expand the sequence array to match
function changeGridAndPattern() {
    seq.subdivision = "16n";
    seq.events = ["C4", "D4", "E4", "F4", "G4", "A4", "B4", "C5"];
}

Updating both properties simultaneously ensures your sequence remains musically coherent and fits perfectly within your grid structure.