Why Call Tone.start Before Triggering Audio in Tone.js
When working with the Tone.js web audio library, you must call
Tone.start() before any audio can be produced. This article
explains the underlying browser security policies—specifically Autoplay
restrictions—that necessitate this function call, how it interacts with
the Web Audio API, and how to properly implement it within a user
interaction event handler to ensure seamless audio playback.
Web Audio API and Browser Autoplay Policies
The requirement to call Tone.start() is not an arbitrary
limitation of Tone.js, but rather a direct response to modern web
browser security standards. To prevent websites from annoying users with
unexpected, loud, or intrusive audio upon loading a page, web browsers
enforce strict autoplay policies.
Under the hood, Tone.js relies on the browser’s native Web Audio API,
which manages sound through an object called the
AudioContext. By default, browsers initialize this
AudioContext in a suspended state. Until the user
explicitly interacts with the page, the browser blocks the audio context
from resuming, keeping the application completely silent.
What Tone.start() Does
Tone.start() is a built-in helper function that attempts
to resume the suspended AudioContext managed by Tone.js.
Calling this function sends a signal to the browser to transition the
audio state from “suspended” to “running.”
Once the context is running, Tone.js can successfully synthesize
sounds, play audio files, and process effects. If you attempt to trigger
a synthesizer, sampler, or player before calling
Tone.start(), the underlying Web Audio API will ignore the
playback commands, resulting in total silence and warnings in your
developer console.
The Necessity of a User Gesture
For Tone.start() to work successfully, it must be
triggered directly by a user interaction, such as a click, tap, or
keypress. Browsers will reject any attempt to resume the audio context
if it is called automatically on page load or inside an asynchronous
callback not directly tied to a user event.
The standard way to implement this is to bind
Tone.start() to a “Start” or “Play” button. Once the
promise returned by Tone.start() resolves, you can safely
trigger your synthesizer notes or start your audio transport.
// Example implementation
const button = document.querySelector('button');
button.addEventListener('click', async () => {
// Resume the AudioContext
await Tone.start();
console.log('Audio context is active');
// Play a synth note safely
const synth = new Tone.Synth().toDestination();
synth.triggerAttackRelease("C4", "8n");
});By ensuring Tone.start() is called within a
user-initiated event, you satisfy the browser’s security requirements,
allowing your Tone.js applications to play audio reliably across all
modern devices.