Audio
Browser games use the Web Audio API: decode samples once, play many overlapping instances cheaply, route through gain / panner / filter nodes for effects. Mobile / desktop engines all expose similar concepts.
Web Audio sfx + music
EXAMPLE
// One-time setup
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const master = audioCtx.createGain();
master.gain.value = 0.8;
master.connect(audioCtx.destination);
// Most browsers require a user gesture before audio can start
document.addEventListener('pointerdown', () => audioCtx.resume(), { once: true });
// 1) Decode + cache samples
const samples = new Map();
async function load(name, url) {
const buf = await fetch(url).then(r => r.arrayBuffer());
samples.set(name, await audioCtx.decodeAudioData(buf));
}
await Promise.all([
load('jump', '/sfx/jump.ogg'),
load('hit', '/sfx/hit.ogg'),
load('music', '/music/level1.ogg'),
]);
// 2) Play a one-shot sample (overlapping calls work fine)
function play(name, { volume = 1, rate = 1, pan = 0 } = {}) {
const src = audioCtx.createBufferSource();
src.buffer = samples.get(name);
src.playbackRate.value = rate;
const g = audioCtx.createGain();
g.gain.value = volume;
const p = audioCtx.createStereoPanner();
p.pan.value = pan;
src.connect(g).connect(p).connect(master);
src.start();
return src;
}
play('jump', { rate: 1 + (Math.random() - 0.5) * 0.1 }); // tiny pitch variation
// 3) Looped background music with cross-fade
let currentMusic = null;
function playMusic(name, fadeIn = 1.5) {
const src = audioCtx.createBufferSource();
src.buffer = samples.get(name);
src.loop = true;
const g = audioCtx.createGain();
g.gain.value = 0;
g.gain.linearRampToValueAtTime(1, audioCtx.currentTime + fadeIn);
src.connect(g).connect(master);
src.start();
if (currentMusic) {
currentMusic.gain.linearRampToValueAtTime(0, audioCtx.currentTime + fadeIn);
setTimeout(() => currentMusic.src.stop(), fadeIn * 1000);
}
currentMusic = { src, gain: g };
}
playMusic('music');
// 4) Master mute / volume
function setMaster(vol) { master.gain.linearRampToValueAtTime(vol, audioCtx.currentTime + 0.1); }
Why it matters
Vary pitch slightly on repeated sounds (jumps, footsteps) — 0.95–1.05 on playbackRate. The same clip stops sounding mechanical and feels alive at zero asset cost.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// Web Audio API
const audioCtx = new AudioContext();
async function playSound(url) {
const buf = await audioCtx.decodeAudioData(await (await fetch(url)).arrayBuffer());
const src = audioCtx.createBufferSource();
src.buffer = buf; src.connect(audioCtx.destination); src.start();
}
Try it Yourself »
Discussion
Loading…