Web Audio processes sound as a graph of nodes in an AudioContext. Sources (oscillators, decoded buffers, media elements, microphone streams) flow through effects (GainNode, BiquadFilterNode, ConvolverNode reverb, PannerNode for 3D) into ctx.destination, and an AnalyserNode can tap the signal for visualizers. The graph runs on a real-time audio thread, and every AudioParam can be automated with sample accuracy.

const ctx = new OfflineAudioContext({ numberOfChannels: 2, length: 48000, sampleRate: 48000 });
const osc = new OscillatorNode(ctx, { type: 'sawtooth', frequency: 440 });
const gain = new GainNode(ctx, { gain: 0 });
osc.connect(new BiquadFilterNode(ctx, { type: 'lowpass', frequency: 1000 })).connect(gain)
.connect(new StereoPannerNode(ctx, { pan: -0.5 })).connect(ctx.destination);
gain.gain.linearRampToValueAtTime(0.8, 0.05).exponentialRampToValueAtTime(0.01, 1); // envelope
osc.start();
const buffer = await ctx.startRendering(); // 1 s, rendered faster than real time
const peak = (ch) => Math.max(...buffer.getChannelData(ch).map(Math.abs));
const [left, right] = [peak(0), peak(1)]; // 0.669 and 0.277: panned leftFor live sound use new AudioContext() and resume() it in a click, since autoplay policies start it suspended. Write custom DSP as an AudioWorkletProcessor, not the deprecated ScriptProcessorNode.