Using the Web Audio API in JavaScript: Full Guide
Learn how to generate, process, and play sound programmatically with the Web Audio API. Create oscillators, connect nodes, and control volume and frequency.
The Web Audio API lets you generate, process, and play sound entirely in JavaScript without any audio files. You build an audio graph by connecting nodes together, like patching cables on a physical synthesizer. The API is powerful enough to build music apps, game sound effects, and audio visualizers.
Here is the simplest sound you can make, a 440 Hz beep that plays for one second:
const audioContext = new AudioContext();
const oscillator = audioContext.createOscillator();
oscillator.type = "sine";
oscillator.frequency.value = 440;
oscillator.connect(audioContext.destination);
oscillator.start();
oscillator.stop(audioContext.currentTime + 1);The oscillator generates a sine wave at 440 Hz (the note A). The connect method sends its output to the audio context's destination, which is your speakers. Calling start begins playback, and stop schedules it to end after one second.
The audio graph model
Every sound in the Web Audio API flows through a graph of connected nodes. You create source nodes that produce audio, processor nodes that modify it, and a destination node that outputs to the speakers.
The oscillator produces a raw waveform. The gain node adjusts the volume. The destination represents your speakers or headphones. You can insert any number of processing nodes between the source and destination, including filters, delays, compressors, and analyzers.
Creating and controlling an oscillator
The OscillatorNode generates a periodic waveform. The type property sets the waveform shape, and the frequency property sets the pitch in Hertz.
const audioContext = new AudioContext();
const oscillator = audioContext.createOscillator();
oscillator.type = "square";
oscillator.frequency.value = 220;The available waveform types are sine (smooth, pure tone), square (hollow, video game-like), sawtooth (buzzy, bright), and triangle (softer than square). Each sounds different because of its harmonic content.
To play the oscillator, connect it to the destination and call start. The stop method takes a time in seconds from the audio context's currentTime.
oscillator.connect(audioContext.destination);
oscillator.start();
oscillator.stop(audioContext.currentTime + 2);You can change the frequency while the oscillator is playing by setting frequency.value at any time. This creates pitch bends and gliding effects without restarting the sound.
Controlling volume with a gain node
A GainNode adjusts the amplitude of the audio passing through it. The gain property works like a volume knob, where 1 is the original volume, 0 is silence, and values above 1 amplify.
const gainNode = audioContext.createGain();
gainNode.gain.value = 0.5;
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);By inserting the gain node between the oscillator and the destination, you control the output volume without changing the oscillator itself. The gain value can be animated over time for fade-in and fade-out effects.
Handling the autoplay policy
Modern browsers require a user gesture before playing audio. The AudioContext starts in a suspended state, and calling start on an oscillator has no effect until the context is resumed.
const audioContext = new AudioContext();
document.querySelector("#playButton").addEventListener("click", () => {
audioContext.resume().then(() => {
const oscillator = audioContext.createOscillator();
oscillator.connect(audioContext.destination);
oscillator.start();
});
});The resume method returns a promise that resolves when the context is allowed to play. Always call resume inside a click or keypress handler. Creating the AudioContext early and resuming it on interaction is the standard pattern.
Putting it together: a simple synth
Combining an oscillator, gain node, and user interaction produces a basic playable synthesizer. The frequency changes with each click.
const audioContext = new AudioContext();
const gainNode = audioContext.createGain();
gainNode.gain.value = 0.3;
gainNode.connect(audioContext.destination);
function playNote(frequency) {
const oscillator = audioContext.createOscillator();
oscillator.type = "sine";
oscillator.frequency.value = frequency;
oscillator.connect(gainNode);
oscillator.start();
oscillator.stop(audioContext.currentTime + 0.5);
}Each note creates a new oscillator, plays it for half a second, and lets it stop. Oscillators are single-use. After calling stop, you cannot restart them. Create a new one for each note.
Common mistakes
- Forgetting to resume the AudioContext. Sound will not play until the context is running, which requires a user gesture.
- Calling start without first calling connect. An unconnected oscillator produces audio that goes nowhere.
- Trying to restart a stopped oscillator. Oscillators are one-shot. Create a new one for each sound.
- Setting gain values too high. Values above 1 amplify the signal, which can cause distortion. Keep gain at or below 1 for clean output.
- Creating too many AudioContexts. One context per page is enough. Create it once and reuse it.
Related articles
- JavaScript Notifications API: Complete Tutorial -- another browser API that requires user permission
- How to Add Event Listeners in JS: Complete Guide -- the click handling pattern used to resume the audio context
- Handling Click Events in JavaScript: Full Guide -- user gesture handling for audio playback
Quick reference
| Operation | Code |
|---|---|
| Create context | const ctx = new AudioContext() |
| Create oscillator | ctx.createOscillator() |
| Create gain node | ctx.createGain() |
| Connect nodes | source.connect(destination) |
| Play sound | oscillator.start() |
| Stop sound | oscillator.stop(ctx.currentTime + seconds) |
| Resume context | await ctx.resume() (requires user gesture) |
| Set volume | gainNode.gain.value = 0.5 |
Rune AI
Key Insights
- Create an AudioContext as the environment for all audio processing.
- OscillatorNode generates synthetic waveforms at a given frequency.
- Connect nodes together using the connect method to build an audio graph.
- GainNode controls volume at any point in the graph.
- The AudioContext starts suspended. Resume it from a user gesture before playing sound.
Frequently Asked Questions
Why do I need a user gesture to start the AudioContext?
Can I play an MP3 file with the Web Audio API?
What is the difference between an oscillator and an audio buffer?
Does the Web Audio API work on mobile?
Conclusion
The Web Audio API models sound as a graph of connected nodes. An AudioContext is the canvas. Oscillators and buffer sources generate sound. Gain nodes control volume. Destination nodes send sound to the speakers. Connect nodes together in a chain and call start to play. This simple model scales from a beep to a full synthesizer.
More in this topic
Using Decorators for Logging in JS Architecture
Learn how JavaScript decorators wrap class methods to add logging without touching the original code, including setup with Babel and how execution order works.
JavaScript While Loop Explained: A Complete Guide
A while loop repeats code as long as a condition stays true. Learn the syntax, see practical examples, and understand when a while loop is the right choice over a for loop.
JavaScript Loops Tutorial: for, while, do while
Loops let JavaScript repeat code without writing it twice. Learn the three core loop types, what each one does, and when to choose one over another.