A modern Web Audio framework for building interactive music apps.
Tone.js wraps the low-level Web Audio API with musician-friendly constructs (Synth, Sampler, Transport), giving you DAW-like power in the browser
// play a middle-C for an 8th-note
import * as Tone from "tone";
const synth = new Tone.Synth().toDestination();
await Tone.start(); // user-gesture gate
synth.triggerAttackRelease("C4","8n");
Grab via NPM, ESM import, or UNPKG script tag.
NPM / Yarn
npm install tone # latest stable
npm install tone@next # bleeding edge
ESM import
import * as Tone from "tone";
UNPKG CDN
<script src="https://unpkg.com/tone"></script>
π Remember to call Tone.start() after a user event before making sound
Synths, Players, the Signal chain, and the global Transport.
The Tone.getTransport() clock synchronises every scheduled callback. Start, stop, loop, change tempo, and quantise events reliably
// quarter-note loop on C2
const synth = new Tone.FMSynth().toDestination();
const loop = new Tone.Loop((time)=>{
synth.triggerAttackRelease("C2","8n",time);
},"4n").start(0);
Tone.getTransport().bpm.value = 120;
Tone.getTransport().start();
Create basses, leads or poly pads instantly with built-ins like Synth, FMSynth, and PolySynth.
Load single shots via Player, or multi-sample instruments with Samplerβcomplete with pitch-shifting
Insert or parallel-route any number of effects (Delay, Reverb, Distortion). Patch nodes exactly like physical cables.
// serial effect chain
const player = new Tone.Player("loop.mp3").start();
const delay = new Tone.FeedbackDelay("8n",0.4).toDestination();
player.connect(delay);
Latency, custom nodes, and AudioWorklets.
Latency Hints β pass { latencyHint:"playback" }
to minimise glitching on mobile.
AudioWorklet Support β v14.7 introduced direct AudioWorkletNode constructors for DSP in a worker
Custom Param Interpolations β drive any Signal with ramps, curves, or external LFOs.
Play your synths with real hardware.
// Basic Web-MIDI β Tone synth
if (navigator.requestMIDIAccess){
const synth = new Tone.PolySynth(Tone.Synth).toDestination();
navigator.requestMIDIAccess().then(access=>{
access.inputs.forEach(inp=>{
inp.onmidimessage = e=>{
const [cmd,note,vel] = e.data;
if (cmd===144 && vel>0) synth.triggerAttack(Tone.Frequency(note,"midi"));
if (cmd===128 || vel===0) synth.triggerRelease(Tone.Frequency(note,"midi"));
}
});
});
}
Render in real-time or offline to WAV.
Use Tone.Recorder for live capture, or Tone.Offline to render faster-than-real-time for glitch-free bounces.
// live record
const rec = new Tone.Recorder();
synth.connect(rec);
await rec.start();
await Tone.start();
synth.triggerAttackRelease("C4","2n");
const wav = await rec.stop();
download(wav,"take-1.wav");
Keep CPU low and timing tight.
Batch sample loads with Tone.loaded().
Dispose unused nodes via .dispose() to free memory.
Query Tone.context.baseLatency to adapt buffer sizes per device.
Biggest API shifts and fresh features.
Feature | Notes |
---|---|
Typescript Migration | Entire codebase now ships with .d.ts for intellisense |
AudioWorklet Support | Real-time DSP off the main thread |
Destination rename | Master β Destination for clarity |
Param Improvements | Signals are simpler (SyncedSignal) and more powerful. |
Docs, examples, and inspiring tutorials.
Swipe horizontally to explore Tone-powered apps.