Tone.js Step Sequencer Tutorial with Tone.Sequence

This tutorial provides a quick, hands-on guide to building a basic web-based step sequencer loop using Tone.js. You will learn how to initialize a synthesizer, configure the Tone.Sequence class to schedule and play musical notes, and control the global audio transport to loop your sequence seamlessly in the browser.

1. Import Tone.js and Create a Play Button

Modern browsers require a user interaction (like a click) to start audio. Create a simple HTML button to initialize and start the sequencer:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Tone.js Step Sequencer</title>
    <!-- Import Tone.js via CDN -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
</head>
<body>
    <button id="start-btn">Play Sequencer</button>

    <script>
        // JavaScript code will go here
    </script>
</body>
</html>

2. Set Up the Instrument

Inside your <script> tag, initialize a basic synthesizer and connect it to the main audio destination (your speakers):

const synth = new Tone.Synth().toDestination();

3. Define the Sequence Array

Create an array representing the steps of your sequencer. Each element in the array represents a step in time. You can use note names (like “C4”) for active steps, and null to represent rests:

const notes = ["C4", "E4", "G4", "B4", null, "A4", "F4", "D4"];

4. Initialize Tone.Sequence

The Tone.Sequence object takes three arguments: 1. Callback function: Executed at every step. It receives the current time (crucial for precise scheduling) and the note value from your array. 2. Events array: The sequence array you defined in the previous step. 3. Subdivision: The duration of each step (e.g., "8n" for eighth notes).

const seq = new Tone.Sequence((time, note) => {
    // Only play a sound if the step is not a rest (null)
    if (note !== null) {
        synth.triggerAttackRelease(note, "8n", time);
    }
}, notes, "8n");

5. Control the Transport and Start Audio

To hear the sequence, you must start both the individual sequence and Tone.js’s global timing system, Tone.Transport. Wrap this logic inside a click event listener to comply with browser audio policies:

document.getElementById("start-btn").addEventListener("click", async () => {
    // Start the Tone.js audio context
    await Tone.start();
    console.log("Audio context active");

    // Configure loop points and tempo (optional)
    Tone.Transport.bpm.value = 120; // Set tempo to 120 BPM

    // Start the sequence at the beginning of the transport timeline
    seq.start(0);

    // Start the transport timeline
    Tone.Transport.start();
});

Once you click the button, Tone.js will continuously loop through your array, playing the defined notes at the specified 120 BPM tempo.