ToneJS

Royce Taylor
6 min readOct 24, 2022

--

Making music with JavaScript sounds like a silly and improbable suggestion, yet with ToneJS , it’s a reality! ToneJS is a JavaScript library that gives a developer access to functions, and objects, that act as the different components for a DAW, (digital, audio, workspace). This library isn’t to be confused with something like TidalCycles or Gibber, which are meant for live coding sounds in real time, instead of creating user based interactions with sound, which is what ToneJS is meant for. ToneJS was created by Yotam Mann (starting since 2014) to create interactive sounds available in the browser.

I started the project while building Echo. After years of working with the Web Audio API, I wanted to encapsulate and reuse some of the code that I was repeatedly writing. The project quickly grew as I got excited about the potential for creating more sophisticated songs entirely the browser. — Yotam Mann

To begin using ToneJS you would need to install it into your environment, to gain access to the library via npm install tone@next . Then you can simply import it with import * as Tone from ‘tone’ in the file that you are expecting to use it in. Now that you have access, for each ‘sound’ or ‘voice’, for which there are many in ToneJS, you will need to connect that sound to an ‘instance of’ the Tone object, followed by the key name for said sound, like so:

const synth = new Tone.Synth();

This alone won’t do anything besides giving you access to the sounds that are available within that main voice. You will also need to plug it in to a master output, just like you would need to plug your guitar into an amp. To do so ToneJS has a ‘toDestination()’ method that creates this output for your sound. Using the Tone object that we have above would look something like this :

synth.toDestination() , or alternatively :

const synth = new Tone.Synth().toDestination

Now this is still just initial setup, to make the sounds happen you will need to utilize ‘triggerAttackRelease()’ method. This method takes in two main arguments, the first one representing the note that you want to play, and the second representing the length of the note.

synth.triggerAttackRelease('C4', '8n');

The first note will be ‘middle C’, otherwise known as ‘perfect C’ (the C that’s in the center of most keyboards), and the second argument, ‘8n’, will denote that the tone will be an eighth note. Now if you were to add this function into an event handler, and then trigger that event, you will hear the beautiful C tone radiate through your speakers!

Can you hear it??

Pretty neat, but let’s say you want to write an entire phrase or ‘measure’ of music to either be looped, or played once, or whatever you want to do (because that’s how art is!). Well we can pull this maneuver off with a bit of clever JavaScript and a little finger grease! We would start as we would with the first example, by creating an instance of a synth, and hook it up to the master output. But instead of hardcoding notes in for the ‘triggerAttackRelease’, lets create and array of notes to make a reference for.

const notes = [“C4”, “E4”, “G4”, “C5”, “E5”, “G5”]; // basic major                       //                                                   chord arpeggio

Now that we have some notes, we can utilize the scheduling method that ToneJS offers to and set it to the Tone instances ‘Transport’ property, which exist similarly to an events target body. Doing so will create a loop for us by defining the ‘scheduleRepeat()’ function which will take in a callback that we can pass a parameter to represent the time of the Tone phrase, and simple call a method, called ‘repeat’ (which we’ll define later in this exercise) on the time that’s being passed in. This callback function will also take in a parameter to keep track of the length of each note that gets passed in.

Tone.Transport.scheduleRepeat((time) => {
repeat(time);
}, "8n");

Hold up, we’re not finished yet! Now I need to show you how to control the BPM of any phrase, for each note may have it’s own length, but it’s within the context of the pacing for the entire phrase. To do so we access The Tone instance’s Transport property again, but then it’s ‘bpm’ property, then it’s ‘value’.

Tone.Transport.bpm.value = 90;

As you can see here we set the BPM to 90 beats per minute, which is a driving, but moderate pace. For reference, the ‘Stars and Stripes’ march tune is measured at 120 BPM. This is all according to preference though of course, and I suggest experimenting with different tempos to figure out what you want your result to be.

Okay so now that we have that, let’s define the repeat function so that it has context to work with. Outside of the functions scope, we need to declare an ‘index’ variable and set it equal to ‘0’, which we will increment in the repeat method. We do so with a function declaration and in the body we create a ‘note’ variable which will be referencing the notes array at each specific location for the length of the array. Then we simply call the ‘triggerAttackRelease’ method passing in the note, the rhythm reference, and the time value that is being passed in. Then we simply increment the index, so that we can iterate through our notes array respectively. Here’s what that will look like:

function repeat(time) {
let note = notes[index % notes.length];
synth.triggerAttackRelease(note, "8n", time);
index++;
}

Now to cap it all off we just need to call the ‘start()’ method, which exists on the Tone’s, ‘Transport’ property. This will actually initialize the start of the phrase for a user to listen to.

Tone.Transport.start();

I can hear it now! That classic major chord progression, written in JavaScript!

Now if we want to make the sound stop after some amount of time, we could do so with a ‘setTimeout()’ (alternativly , one could implement the method in another user event) . So we simply just need to access the Transport’s ‘stop()’ method, the same way we accessed it’s ‘start()’.

Tone.Transport.stop();

Throw it in a setTimeout, along with the amount of time that you want it to stop playin at, and you have a complete musical phrase. Here is what all of this looks like together:

const synth = new Tone.Synth();
synth.toDestination();
const userEvent = () => { // wrapped in a user event

const notes = ["C4", "E4", "G4", "C5", "E5", "G5"];
let index = 0;
Tone.Transport.scheduleRepeat((time) => {
repeat(time);
}, "8n");
Tone.Transport.bpm.value = 90;function repeat(time) {
let note = notes[index % notes.length];
synth.triggerAttackRelease(note, "8n", time);
index++;
}
Tone.Transport.start();
setTimeout(() => {
Tone.Transport.stop();
}, 5000);
};

This example is a mere fraction of ToneJS’s capabilities, of course, but it’s a decent starting point to get you started with making music with JavaScript. There are other synthesized sounds, built in sequencers, oscillators and tools that come out of the box with ToneJS that has the potential for a developer to program an entire DAW, with most of the bells and whistles for composing music.

Resources:

--

--