Tone.js
Tone.js is a comprehensive Web Audio framework designed for creating interactive music directly within the browser. It provides high-level abstractions over the Web Audio API, offering a wide array of instruments (like Synths, Samplers), effects (Reverb, Delay), and advanced signal processing tools for dynamic audio manipulation. Currently stable at version 15.1.22, the library has seen consistent development, including a significant transition to TypeScript in its 14.7.x series, enhancing type safety and developer experience. Its release cadence is moderate, with major updates introducing new features and improvements, while also addressing breaking changes through version increments. Key differentiators include its robust scheduling system (`Tone.Transport`), extensive collection of DSP modules, and focus on real-time interactive performance, making it a popular choice for web-based musical applications and installations.
Common errors
-
TypeError: Failed to execute 'connect' on 'AudioNode': parameter 1 is not of type 'AudioNode'.
cause Attempting to connect a native Web Audio `AudioNode` directly to a Tone.js node using the native `.connect()` method, which is no longer overwritten by Tone.js since v13.8.25.fixUse the `Tone.connect()` helper function for connecting native Web Audio nodes with Tone.js nodes: `Tone.connect(nativeNode, toneNode);`. -
ReferenceError: require is not defined
cause Using CommonJS `require()` syntax in a modern project configured for ES Modules, or when Tone.js is intended to be imported as an ES Module.fixSwitch to ES Module import syntax: `import * as Tone from 'tone';` or `import { Synth } from 'tone';`. Ensure your `package.json` specifies `"type": "module"` if it's an ESM-only project. -
Argument of type '"4n"' is not assignable to parameter of type 'TimeExpression | Object<string, number>'.
cause Attempting to use a deprecated string expression for time/duration where an object notation or a different TimeExpression format is expected, especially after v13.4.9.fixUpdate time expressions to use object notation or compose them arithmetically. For simple cases, `Tone.Time('4n')` would convert, or use `{ '4n': 1 }` directly as a duration parameter if the API accepts it. -
The AudioContext was not allowed to start. It must be resumed (or created) after a user gesture on the page. https://goo.gl/7K7WLu
cause Attempting to play audio or start the Tone.js context before the user has interacted with the page, which is a browser security/policy restriction.fixWrap your `Tone.start()` call in a user-triggered event listener, such as a click handler: `document.getElementById('playButton').addEventListener('click', async () => { await Tone.start(); /* ... your audio code ... */ });`.
Warnings
- breaking The way native Web Audio nodes connect to Tone.js nodes was changed in v13.8.25. `AudioNode.prototype.connect` is no longer overwritten, meaning direct `.connect()` calls between native and Tone.js nodes will fail.
- breaking String expressions for `Tone.TimeBase` and related classes (e.g., '4n', '8t') were deprecated in v13.4.9 in favor of object notation. Direct string usage as time values may still work but is not the recommended or most performant approach for complex expressions.
- gotcha Browsers require a user interaction (like a click or key press) to start the `AudioContext`. If `Tone.start()` is not called within an event listener triggered by user input, no audio will play.
- breaking The global `Master` output was renamed to `Destination` in a pre-14.7.x release. Code referring to `Tone.Master` will no longer work.
- breaking Tone.js was converted to TypeScript starting from version 14.7.x. While this improves type safety, it might affect existing JavaScript projects that relied on specific build processes or inferred types, and can sometimes lead to module resolution issues if not configured correctly.
Install
-
npm install tone -
yarn add tone -
pnpm add tone
Imports
- Tone
const Tone = require('tone');import * as Tone from 'tone';
- Synth
import { Synth } from 'tone/build/esm/instrument/Synth';import { Synth, Destination, start } from 'tone'; - start
Tone.start(); // after import * as Tone from 'tone'
import { start, Transport } from 'tone';
Quickstart
import { Synth, Destination, start, now, Transport } from 'tone';
const playButton = document.createElement('button');
playButton.textContent = 'Play Synth';
document.body.appendChild(playButton);
playButton.addEventListener('click', async () => {
// Start the Tone.js audio context on user interaction
await start();
console.log('Audio context started.');
// Create a simple synthesizer
const synth = new Synth().toDestination();
// Schedule some notes
const synthNotes = [
{ note: 'C4', time: 0, duration: '8n' },
{ note: 'E4', time: '8n', duration: '8n' },
{ note: 'G4', time: '4n', duration: '8n' },
{ note: 'C5', time: '2n', duration: '8n' }
];
Transport.scheduleOnce(() => {
synthNotes.forEach(event => {
synth.triggerAttackRelease(event.note, event.duration, now() + Transport.seconds + event.time);
});
}, 0); // Schedule immediately when transport starts
// Start the transport to play scheduled events
Transport.start();
console.log('Synth playing...');
});