Web Audio
The Web Audio API is not for playing files -- it is a signal processing graph. Sources feed into effects, effects chain into each other, and the result goes to your speakers. AudioContext, AudioNode, and AudioWorklet are the three concepts that unlock the whole thing.
I always assumed audio in the browser meant loading a file and calling .play(). The Web Audio API turns out to be something else entirely.
It is a signal processing graph. You create nodes, you wire them together, and audio flows through the chain from source to output. You can generate sound from scratch. You can build reverb, filters, and 3D spatial positioning. You can analyze a waveform in real time. None of it requires loading a file.
AudioContext: The Runtime
Everything in the Web Audio API lives inside an AudioContext. It owns the audio hardware connection, the sample rate, and the timing clock.
const ctx = new AudioContext()One important constraint: browsers require a user gesture before allowing audio to start. Creating an AudioContext on page load is allowed, but audio will not actually play until the user has clicked, tapped, or otherwise interacted with the page. If you try to call ctx.resume() or start a node before that, most browsers will refuse silently.
The fix is to create the context (or call ctx.resume()) inside a click handler.
AudioNode: The Graph
Every piece of audio processing is an AudioNode. Nodes have inputs and outputs. You connect them with .connect() and audio flows forward:
const osc = ctx.createOscillator() // a source node
const gain = ctx.createGain() // an effect node
osc.connect(gain)
gain.connect(ctx.destination) // ctx.destination = your speakersThe shape of this graph determines the sound. A simple chain -- oscillator to gain to destination -- produces a pure tone at controlled volume. A more complex graph can add reverb, spatial positioning, frequency analysis, or filtering.
Source nodes produce audio:
OscillatorNode-- generates a continuous waveform (sine, square, sawtooth, triangle) at a given frequencyAudioBufferSourceNode-- plays a decoded audio buffer from memory (useful for pre-loaded samples)MediaStreamSourceNode-- wraps agetUserMediamicrophone stream as an audio source
Effect nodes process audio flowing through them:
GainNode-- scales amplitude (volume control)PannerNode-- positions a sound source in 3D space; combine withAudioListenerfor full spatial audioBiquadFilterNode-- applies EQ filters (low-pass, high-pass, band-pass, etc.)AnalyserNode-- taps the signal to extract frequency or time-domain data for visualization without modifying it
Making Sound
Here is a complete example -- a one-second tone at A4 (440 Hz):
const ctx = new AudioContext()
const osc = ctx.createOscillator()
osc.type = 'sine'
osc.frequency.value = 440
const gain = ctx.createGain()
gain.gain.value = 0.2 // keep it quiet
osc.connect(gain)
gain.connect(ctx.destination)
osc.start()
osc.stop(ctx.currentTime + 1) // stop after 1 secondctx.currentTime is a high-precision clock in seconds. Scheduling with it instead of setTimeout gives sample-accurate timing -- critical for anything musical.
AudioParams are automatable. The gain.gain and osc.frequency properties are not plain numbers. They are AudioParam objects that can be scheduled to change over time:
// Fade out over 2 seconds
gain.gain.setValueAtTime(0.3, ctx.currentTime)
gain.gain.linearRampToValueAtTime(0, ctx.currentTime + 2)This is how you build envelopes, vibrato, and filter sweeps without polling in JavaScript.
ExpandWeb Audio API: AudioNode pipeline from source nodes through effect nodes to AudioDestinationNode, plus AudioWorklet and output options
AudioWorklet: Custom Processing in a Dedicated Thread
The original Web Audio API ran all processing on the main audio thread managed by the browser. The newer AudioWorkletNode lets you write custom DSP code that runs in a dedicated, real-time audio thread -- separate from the main JavaScript thread and separate from the browser's rendering thread.
// Register a custom processor
await ctx.audioWorklet.addModule('my-processor.js')
const node = new AudioWorkletNode(ctx, 'my-processor')
node.connect(ctx.destination)The processor itself runs in a separate file with AudioWorkletProcessor:
// my-processor.js (runs in audio worklet scope)
class MyProcessor extends AudioWorkletProcessor {
process(inputs, outputs) {
const output = outputs[0]
for (const channel of output) {
for (let i = 0; i < channel.length; i++) {
channel[i] = Math.random() * 0.1 // white noise
}
}
return true // keep processor alive
}
}
registerProcessor('my-processor', MyProcessor)AudioWorklet is the right tool for synthesizers, effects engines, and anything that needs to run custom logic at sample rate without risking audio glitches from main-thread interruption.
Spatial Audio
Three-dimensional sound placement uses PannerNode and AudioListener:
const panner = ctx.createPanner()
panner.positionX.value = 2 // 2 meters right
panner.positionY.value = 0
panner.positionZ.value = -5 // 5 meters ahead
osc.connect(panner)
panner.connect(ctx.destination)
// Move the listener
ctx.listener.positionX.value = 0
ctx.listener.positionY.value = 0
ctx.listener.positionZ.value = 0The browser applies distance-based attenuation and stereo panning automatically. Useful for games, immersive experiences, and any scenario where sound direction matters.
Green Tier
Web Audio is green tier. Chrome, Firefox, Safari, and Edge all support it. It works on desktop and mobile, including iOS Safari.
if (typeof AudioContext !== 'undefined') {
// Web Audio is available
}The permission model does not apply here -- no dialog appears for audio output. The user gesture requirement is enforced silently instead.
Web MIDI pairs naturally with Web Audio: use MIDI input to drive oscillator frequency and velocity, and Web Audio to synthesize the actual sound.
Web Audio generates and processes sound. The protocol for communicating musical events to hardware is separate -- that is Web MIDI, and it pairs with Web Audio to turn the browser into a synthesizer.
The Essentials
AudioContextis the runtime. All nodes belong to one context. Create it inside a user gesture handler or callctx.resume()there.- Audio is a graph of nodes. Source nodes produce audio. Effect nodes transform it.
ctx.destinationis the output. Connect with.connect(). AudioParamvalues can be scheduled withsetValueAtTime()andlinearRampToValueAtTime()for sample-accurate automation.AudioWorkletNoderuns custom DSP in a real-time audio thread -- separate from the main JS thread. Use it for synthesizers and custom effects that cannot afford main-thread jitter.PannerNode+AudioListener= 3D spatial audio. The browser calculates distance attenuation and stereo panning automatically.- Green tier -- Chrome, Firefox, Safari, Edge. The user gesture requirement is enforced by the browser, not the permission dialog system.
Further Reading and Watching
- Web Audio API - createOscillator and Audio Nodes EXPLAINED (YouTube) -- builds a sound from scratch using AudioContext, oscillator, gain, and destination nodes with a clear walkthrough of how the graph connects
- Web Audio API -- MDN -- full reference covering every node type, AudioParam scheduling, AudioWorklet setup, and browser support details