// ALL TRANSMISSIONS
OYUN TASARIMI·10 DK OKUMA·30 Haziran 2026

How to Build Reactive Audio in the Browser: A Web Audio API Guide

Reactive sound design isn't continuous ambient — it's event-triggered, information-bearing sound. Step-by-step on how to do it in a browser game with the Web Audio API.

#web audio#reactive audio#sound design#javascript#game development#how-to
// WAVEFORM — #1F0C — SIGNAL_PITCH/EDITORIAL
▶ ŞİMDİ DENE // SIGNAL PITCH

Reactive sound is not two things: continuous music or random effect. Reactive sound is triggered by an event inside a game, carries information, then goes silent. This article shows how to do it in the browser — with code and principles.

1. The base: AudioContext

The center of the Web Audio API is AudioContext. Create it once, let it live for the game's lifetime. It can't start without user interaction (autoplay policy) — wait for a "START" button click.

const ctx = new AudioContext();
document.querySelector("#start")?.addEventListener("click", () => {
  if (ctx.state === "suspended") ctx.resume();
});

2. Audio files: preload, decode

Loading a file when the event fires is too late. fetch the bytes, turn them into an AudioBuffer with decodeAudioData, keep them in memory.

async function loadSample(url: string): Promise<AudioBuffer> {
  const res = await fetch(url);
  const bytes = await res.arrayBuffer();
  return await ctx.decodeAudioData(bytes);
}
const chime = await loadSample("/audio/chime.wav");

3. Playing: BufferSource + Gain

Create a new AudioBufferSourceNode for every sound event (single-use). Control level with GainNode. The connect chain: source → gain → destination.

function playChime(volume = 0.6) {
  const src = ctx.createBufferSource();
  src.buffer = chime;
  const gain = ctx.createGain();
  gain.gain.value = volume;
  src.connect(gain).connect(ctx.destination);
  src.start();
}
The golden rule of reactive sound: silence is default. If nothing is playing, the game is running. Sound means event.

4. Ducking: an important event silences the rest

Route every sound through a master GainNode. When an important event happens (e.g. a goal), drop the master gain to 0.2, then ramp back to 1.0. The pullback of sound creates the drama.

const master = ctx.createGain();
master.connect(ctx.destination);

function duck(durationMs = 800) {
  const now = ctx.currentTime;
  master.gain.cancelScheduledValues(now);
  master.gain.setValueAtTime(master.gain.value, now);
  master.gain.linearRampToValueAtTime(0.2, now + 0.05);
  master.gain.linearRampToValueAtTime(1.0, now + durationMs / 1000);
}

5. Spatialization: positional information

PannerNode tells you where the sound comes from. In a 2D game, feed x,y coordinates to the panner. If danger approaches from the left, the player knows without hearing explicitly.

6. Performance: not ScriptProcessor, AudioWorklet

In modern browsers ScriptProcessorNode is deprecated. If you need custom DSP use AudioWorklet — runs on its own thread, no jank. For most games the built-in nodes are enough.

7. Anti-patterns

  • Loop drone: continuous ambient = no information = boring.
  • Effect on every click: sound inflation, ear fatigue.
  • Same sample on repeat: prepare 3-5 variations, pick at random.
  • Forgetting master gain: a single user mute should toggle the master.

8. Practical checklist

  • Single AudioContext instance, resumed at the game's start
  • All samples loaded up front
  • Ducking through the master gain
  • 3+ variations per effect
  • Silence baseline — your script should have a "when shouldn't there be sound" list

9. Conclusion

Reactive audio gives huge impact for little code. The whole infrastructure fits in 100 lines of Web Audio. The rest is design: deciding which event deserves a sound, when to stay silent. Signal Pitch's doctrine — sound = information — drives that decision.

// CARRIER WAVETarayıcıda oyna
ENGAGE →