Library / SDK
Tonejs/Tone.js avatar
Tonejs/Tone.js

Tone.js: a Web Audio framework for scheduling music in the browser

Project brief: A Web Audio framework for making interactive music in the browser.

14,749 stars1,065 forksTypeScriptMIT

At a glance

What is it?
Tone.js wraps the Web Audio API in a DAW-style transport, synths and sample players. It suits browser instruments and generative pieces, and it is the wrong choice when you only need to fire off a short sound effect.
Who is it for?
Adopt Tone.js if you are building a browser instrument, a step sequencer, or generative music that needs sample-accurate scheduling and a shared clock. Do not adopt it if you only need to play a short sound effect on a click, or if you want a score notation engine, since the README says nothing about notation.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What Tone.js solves for browser audio developers

The Web Audio API gives you sample-accurate scheduling, but it gives it to you in raw nodes: oscillators, gain nodes, buffers, and a clock measured in seconds since the page loaded. Assembling a playable instrument out of that means writing your own envelope logic, your own note allocation, your own scheduling loop. Tone.js exists to remove that assembly work. The README describes it as "a Web Audio framework for creating interactive music in the browser", with an architecture "familiar to both musicians and audio programmers".

The intended audience is narrow and specific. If you are building a step sequencer, a drum machine, a synth playground, or a generative piece that runs in a tab, Tone.js is aimed at you. If you are building a video editor that needs one beep when a render finishes, it is not. The project ships a global transport, prebuilt synths and effects, and lower-level building blocks for people who want to write their own oscillators and control signals. That range is the point: the same library covers the musician who wants Tone.Synth and the audio programmer who wants to wire nodes by hand.

The Transport, Tone.now() and why callbacks receive a time argument

Tone.js has two clocks and the distinction matters more than any other design decision in the library. The first is the AudioContext clock, which the README says "starts at 0 when the page loads and counts up in seconds". Tone.now() reads it. You can schedule against it directly: the README's example plays C4, E4 and G4 at now, now + 0.5 and now + 1.

The second clock is the Transport, returned by Tone.getTransport(). Unlike the AudioContext clock it can be started, stopped, looped and adjusted while running, and the README compares it to the arrangement view in a DAW. Tone.Loop attaches a callback to it with a tempo-relative interval such as "4n" for a quarter note, and every loop starts when the Transport starts.

The part people get wrong is the callback signature. JavaScript callbacks are not precisely timed, so Tone.js passes the sample-accurate event time into the callback as its first argument. The README is blunt about this: use that time value to schedule the events, not the current clock reading. A loop callback that calls triggerAttackRelease without passing the time argument will drift, because it is scheduling against whenever the callback happened to run rather than when the beat actually falls. Time values themselves are flexible: any method taking time accepts a number of seconds or a string, where "4n" is a quarter note, "8t" an eighth-note triplet and "1m" one measure.

Installing Tone.js and playing a first note

The README gives two installation routes. The npm route installs the latest stable version, or the next version if you pass the tag. The unpkg route drops the library into an HTML document with a script tag, which must precede any project scripts.

bash
npm install tone
npm install tone@next

The import is a namespace import, not a default import:

js
import * as Tone from "tone";

Before anything makes sound you must call Tone.start() from inside a user-triggered event listener. The README marks this as IMPORTANT and explains why: browsers will not play any audio until the user clicks something, and scheduling before the AudioContext is running produces silence or incorrect scheduling. Tone.start() returns a promise, and audio is ready only once it resolves.

js
document.querySelector("button")?.addEventListener("click", async () => {
  await Tone.start();
  console.log("audio is ready");
});

With that in place, the smallest useful program is a synth connected to the destination and one note triggered for an eighth-note duration:

js
const synth = new Tone.Synth().toDestination();
synth.triggerAttackRelease("C4", "8n");

The first argument is a frequency in hertz such as 440 or pitch-octave notation such as "D#2". The second is the hold duration, in seconds or as a tempo-relative string. The optional third argument is when along the AudioContext time the note should play, which is how you schedule into the future.

Monophonic synths, PolySynth and the note allocation you do not write

Every prebuilt instrument in Tone.js, including Tone.FMSynth, Tone.AMSynth and Tone.NoiseSynth, is monophonic: one voice, one note at a time. The README states this plainly rather than hiding it behind a default. If you want chords you wrap a monophonic synth in Tone.PolySynth, which handles note allocation for you.

The API stays close to the monophonic version, with one asymmetry worth remembering: triggerRelease takes a note or an array of notes, because the polyphonic synth needs to know which voices to release. The README's example attacks D4, F4, A4, C5 and E5 at half-second intervals, then releases all five at once with an array argument four seconds in. If you pass a single note to a PolySynth release you will only free one voice.

Samples follow the same pattern. Tone.Player loads and plays back one audio file, and Tone.loaded() returns a promise that resolves when all audio files are loaded, which the README offers as a shorthand for waiting on each buffer's onload event individually. Tone.Sampler combines multiple samples into an instrument. The repository's examples directory is the practical reference here: examples/sampler.html, examples/polySynth.html and examples/mixer.html exist alongside about two dozen others, and examples/README.md describes how to run them.

Where Tone.js is the wrong tool

The autoplay restriction is not a Tone.js limitation you can configure away, and it is the first thing that breaks a naive integration. Any Tone.js code that runs at page load, outside a click or keydown handler, will produce nothing. This is a browser policy, and the README treats it as a hard constraint rather than a caveat.

The second boundary is scope. Tone.js is a framework for interactive music, not a general audio utility. Its scheduling model, its transport and its instrument abstractions all assume you are arranging notes over time. If your requirement is to play a single short sound when a user clicks a button, the transport and the polyphonic allocation machinery are overhead you will carry without using. A plain AudioBufferSourceNode or an HTML audio element covers that case with less surface area.

The third is a documentation gap rather than a design flaw. The README covers installation, the synth API, time encodings, starting audio, the transport, instruments and samples. It does not document notation, score import, or MIDI file parsing, so if your project needs to render a score you should not assume Tone.js provides it. The repository layout shows a test/ directory and a scripts/ directory with a changelog generator, but the README says nothing about how to run the test suite against your own fork, so treat the test setup as something to read in the repository rather than something the README explains.

Tone.js compared with the raw Web Audio API and Howler.js

The honest comparison is against the Web Audio API itself, because Tone.js is a layer on top of it and nothing more. If you use the raw API you get the same sample-accurate scheduling and the same node graph, but you also write your own envelope, your own voice allocation and your own event loop. Tone.js gives you those as Tone.Synth, Tone.PolySynth and Tone.Loop. The cost is a dependency and a layer of abstraction between you and the nodes, which matters when you need to reach into the graph for something Tone.js does not expose.

Against Howler.js the split is about what the library is for. Howler is oriented toward playback: load a sound, play it, set its volume and position. Tone.js is oriented toward scheduling and synthesis: notes on a timeline, instruments with envelopes, a transport that can loop and change tempo. If your app is a game that plays effects, Howler's model is a closer fit. If your app is a sequencer, Tone.js is. Choosing Tone.js for simple playback means adopting a transport you never start.

There is also a version question that affects anyone comparing APIs across tutorials. package.json on the dev branch reports version 15.5.36, while the most recent release listed is 15.1.22 from 2026-07-12. The README's installation section offers tone@next precisely because a next channel exists. Pin your version and read the API docs for that version rather than trusting a tutorial written against an older major.

Maintenance, licensing and the cost of upgrading

The repository is not archived, and the last push was on 2026-07-12, which matches the 15.1.22 release date. The release history is uneven rather than steady: 13.8.25 in 2019, 14.7.39 in 2020, then a long gap before 15.1.22 in 2026. That pattern suggests the project moves in larger jumps rather than continuous small releases, so an upgrade is a planned event rather than a routine bump.

Tone.js is MIT licensed. In practical terms that permits commercial and closed-source use, modification and redistribution provided the copyright notice and permission notice are included. That is the usual reading of MIT, not legal advice; if your organisation has a licence review process, run the LICENSE.md file through it rather than relying on this summary.

The upgrade cost is dominated by the major-version jumps visible in the release list. The npm scripts in package.json show a lint step that runs tsc --noEmit and eslint over the Tone directory, plus a web-test-runner suite and a test:readme script that checks the README examples. Those scripts exist for maintainers of the library itself. For a consumer, the meaningful cost is that the API surface is large (synths, effects, control signals, transport) and a major bump can touch any part of it. Read CHANGELOG.md at the repository root before moving between majors.

Editorial conclusion

Adopt Tone.js if you are building a browser instrument, a step sequencer, or generative music that needs sample-accurate scheduling and a shared clock. Do not adopt it if you only need to play a short sound effect on a click, or if you want a score notation engine, since the README says nothing about notation. Before committing, verify three things: that your intended entry point calls Tone.start() inside a real user gesture, that the pieces you need are monophonic Tone.Synth-style instruments or a Tone.PolySynth wrapper, and that the version you install matches the API you are reading about, because package.json on the dev branch reports 15.5.36 while the most recent release listed is 15.1.22.

Frequently asked questions

What does Tone.js do?

It is a Web Audio framework for creating interactive music in the browser, offering a global transport for scheduling events, prebuilt synths and effects, and lower-level building blocks for writing your own instruments.

How to install Tone.js?

The README gives two routes: npm install tone (or npm install tone@next for the next version), or a script tag pointing at unpkg.com/tone placed before any project scripts.

How to use Tone.js?

Call Tone.start() from inside a user-triggered event listener, wait for the returned promise to resolve, then create instruments such as new Tone.Synth().toDestination() and trigger notes against Tone.now() or the Transport.

What is the difference between Tone.js and the Web Audio API?

Tone.js is built on the Web Audio API and abstracts its AudioContext clock, adding a transport that can be started, stopped, looped and adjusted, plus prebuilt synths and effects. The underlying sample-accurate scheduling is the same.

Is Tone.js an alternative to Howler.js?

They target different work. Tone.js is built around scheduling notes on a transport and synthesizing sound, while Howler.js is oriented toward loading and playing back audio files. For a sequencer Tone.js fits; for simple playback it carries machinery you would not start.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/tonejs-tone-js.svg)](https://hysenlabs.com/projects/tonejs-tone-js)