Creating a Step Sequencer with Tone.js

Garrett Bodley
Geek Culture
Published in
5 min readApr 13, 2021

A step sequencer is an electronic device that divides a measure into predetermined subdivisions or “steps”. Each step can then be used to trigger a specific sound and/or note. As explained in this excellent writeup by Monica Dinculescu, having consistent and regularly timed events can be difficult to achieve in Javascript due to it being a single-threaded language. To overcome this, I used a free web audio framework called Tone.js as the basis for my project.

Tone greatly simplifies a number of things when trying to programmatically generate music. The documentation can be a bit confusing though, and I found myself spending hours clicking between pages, trying to understand how to get all the pieces to fit together. I hope to (partially) remedy that issue with this post and help anyone working with Tone in the future. This is not intended to be an all-encompassing explanation of Tone.js but rather a walkthrough of how I approached making my sequencer.

FYI: this post assumes the reader has some familiarity with music theory

Our design process for the step sequencer is as follows:

  1. Create the synths needed to generate sound.
  2. Create a grid consisting of multiple sub-arrays within a parent array. This will function as quasi sheet music and will tell the synths when to play a tone.
  3. Write a loop function that will read the grid notation and trigger the synths accordingly.
  4. Create elements in the DOM to visually represent everything with the ability to interact with the grid to turn notes on and off.

Setting Up the Synths

The most simple and straightforward instrument provided by Tone.js is the Synth. A Synth is monophonic, meaning it can only play one note at a time. To overcome this, we’ll create one Synth per note available, and store them in an array.

Tangential quick and dirty acoustics primer:

The timbre (or individual tonal quality) of any instrument, analog or digital, is determined by both the waveform and the envelope of the generated sound. The waveform refers to what the sound wave looks like on an oscilloscope, whereas the envelope refers to how that sound changes over time. That change can be in terms of volume, pitch, and even the timbre of the sound itself!

Tone.js’s Synth consists of an OmniOscillator routed through an AmplitudeEnvelope. If you’re curious about learning more, the Tone devs have a number of examples available that allow you to play with the different oscillators and tinker with the envelope settings of the synths. I am by no means an acoustics expert, but the Tone library seems to provide more than enough flexibility to generate a wide variety of instrument sounds, which could be easily incorporated into this or any other projects.

Don’t worry at all if this jargon has left you confused and with a headache! Tone’s standard Synth provides more than enough functionality for our needs, so feel free to ignore/skip the preceding two paragraphs and continue on with the tutorial.

Making the Grid

Now that the synths have been created we need to create a structure that will function as a sort of notation. I decided to use nested arrays, with each sub-array corresponding to one row on the sequencer, and thus one of the synths.

Each sub-array contains multiple objects with a note attribute and an isActive boolean to store whether the user has selected that note to be played.

I found it helpful to think of the sub-arrays as being stacked vertically within the parent array, with the vertical axis corresponding to pitch, and the horizontal axis corresponding to time.

visualization of the grid data structure

And the corresponding code:

I’m using an F minor pentatonic scale for my sequencer, as it’s a beginner-friendly scale where all notes will sound good when/if played together. Feel free to change the parameters here. As you’ll see later in the tutorial, our DOM elements are generated programmatically using the grid as a reference, so using more notes, or fewer notes, or changing how many measures you want the sequencer to represent is super quick and easy!

Creating our loop function

Okay, we have our basic grid set up and we have our Synths set up, but how do we combine the two to make music?

Part of the core of Tone.js is the Transport, which allows you to schedule events reliably as well as play and pause audio. We’ll be using the scheduleRepeat method for our sequencer. scheduleRepeat takes a callback function as a prop as well as the time interval or subdivision between executing the callback. It will then repeatedly execute the callback whenever the Transport is playing.

On to our callback function repeat() . The callback checks an external counter to know which step it is currently on. It then iterates through the grid to check if any notes for the current step are activated, and if so schedules an event with the correct synth so that a note will sound. To finish things up the counter is incremented so that we can reference the grid in the correct spot when the callback function is next executed.

Building things out in the DOM

Now that we’ve written the necessary logic for our sequencer we need to build out the visual components the user will interact with. Our sequencer will live inside of a parent div that utilizes a grid layout. The buttons will be generated programmatically and be passed onClick functions that will alter the corresponding note in our grid .

Our event listener takes in the rowIndex and noteIndex so we can find the corresponding note object in the grid and check its state. It also receives the event object so that we can change the styling of the button.

Final Touches: making a play button

All that’s left is to configure a button that plays/pauses the Transport. You have to call Tone.start() before making any music, so I added a conditional bit to the button’s logic to get everything set up the first time the user presses play. Otherwise, you just use a boolean as a flag variable to toggle the start and stop of the Transport.

Behold the sequencer

Photo by Eric Nopanen on Unsplash

Here’s a JS Fiddle with all the parts combined for a basic step sequencer. I hope you found this walkthrough helpful, and wish you well in your future programming (and musical) endeavors!

References, sources, and further reading:

Documentation for Tone.jsWikipedia page on Synthesizers"Tone.js & Codepen" YouTube series made by Jake Albaugh"Making sounds with React and Tone.js" by Nicolai Fredriksen"Let’s Make Music with JavaScript and Tone.js" by Jim Bennet

--

--