iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

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…