AgentGames

Search titles, descriptions, models and tags. Press Enter to see results.

🔊 Guide

Game sound effects, and why your game is silent

No error, no sound, and everything else working. The browser is waiting for a click, and once it has one you can make every effect in code.

Updated 27 October 2026 9 min read By the Agent Games team
Jump to a section
  1. Why your game is silent and nothing says so
  2. Paste this into the game you already have
  3. Sound effects you write instead of download
  4. Short, and then shorter than that
  5. Volume, and the tab mute button
  6. One sound per impact, not one per frame
  7. Music, and whether you want any
  8. Common questions
  9. Where to go next

Your audio code is probably fine. A browser refuses to make a sound until the player has interacted with the page, so an audio context built while the page loads is built dead. Add a start button, build the context inside that click, and then write every effect in code rather than hunting for files.

The rule, in one line

Create or resume the audio context inside the handler for a real click, tap or key press. Not on load, not in a timer, not when a fetch comes back. That one move separates a silent game from a loud one, and it is why nearly every browser game opens with a button.

Why your game is silent and nothing says so#

This fault has no symptom to search for. The console stays clean, the rest of the game runs, and the lines that play a sound run too. The browser is holding the audio context in a suspended state, where work piles up and none of it reaches the speaker.

The rule exists because sound that starts by itself is hostile. A page that blares at you from a background tab is the behaviour browsers spent years stamping out. The answer was to demand a gesture first, and a gesture means a click, a tap or a key press that the person actually made.

  • Nothing is logged. A suspended context is a normal state rather than a fault, so there is nothing to report. Silence next to a clean console points here long before it points at your own code.
  • Everything else works. Scoring, collisions, drawing, saving. Audio alone is missing, because audio alone needs permission.
  • It works if you click first. Click an empty part of the page, then make the game fire a sound. If that one arrives, the gesture was the missing ingredient.
  • It may work for you and nobody else. Browsers treat a site you open twenty times a day differently from a cold first visit, and you are the person opening it twenty times a day.
Where the audio context is born The same two lines, in two places, and only one of them makes a sound. Built on page load const ctx = new AudioContext()The context starts out suspendedSounds are scheduled, none arriveNothing is reported in the console Built in the click btn.onclick = () => ctx.resume()A real gesture wakes the contextEverything after it plays normallyWhich is why games have a start screen If a sound works only when you click the page first, this is the whole bug.
Resuming an existing context counts as well, so you can build the context early and wake it in the click. Building it in the click is simpler to get right.

So the start button is not decoration. It is the only place the sound can legally come alive. Give it the rest of the first-frame work too: read the saved high score, build the level, show the controls. Work hidden behind a button is work the player never sees.

Paste this into the game you already have#

This is written for a game that already runs and makes no noise. It asks for the gesture rule, generated effects, one volume control for all of them and a mute the game remembers.

PromptCopy this, paste it with your game, send it
Add sound to this game. Do not add any audio files and do not add a
library. Every sound is made in code with Web Audio.

GESTURE: nothing may play until the player has interacted with the
page. Put a START button over the game. Create the AudioContext inside
that click handler, call resume() on it, and only then begin play. If
the context is ever suspended afterwards, resume it from the next
click.

MASTER GAIN: one gain node that every sound connects through, and it
is the only thing connected to the destination. Set it near 0.3 so
several effects at once do not clip.

EFFECTS: write one small function taking a waveform, a start
frequency, an end frequency and a duration. Inside it, create an
oscillator and a gain node, connect oscillator to gain to master,
schedule the pitch slide and the gain fade against the context's own
currentTime, then start the node and book its stop. Use it for jump,
coin, hit and game over. Build the explosion from a short buffer of
random samples instead of a tone.

LENGTH: every effect under 200 milliseconds except the explosion and
the game over sound. Ramp the gain down to a tiny value rather than
cutting it off, so nothing clicks at the end.

ONE PER IMPACT: play a sound when a condition becomes true, never
while it stays true. Keep last frame's state and compare. Give
repeating sounds a minimum gap in seconds. Vary the start frequency by
a small random amount so repeats are not identical.

MUTE: a mute button in a corner and the M key. Save the choice in
localStorage and read it back on load, so a player who muted once
stays muted after a reload. Mute sets the master gain to zero rather
than pausing the game.

CONFIG: one object at the top holding the master volume, the mute key,
and the frequency and duration of every effect, each commented.

The gesture paragraph is the one to keep. Everything else is taste, and you will be changing the frequencies within a minute of hearing them. Drop that paragraph and the code still looks right, still runs without errors, and still makes no sound.

Sound effects you write instead of download#

A sound effect is a tone whose pitch moves and whose volume falls away. That is the whole of a coin, a jump or a thud, and a browser can build one out of nothing. No files, no asset folder, no second request, nothing to go missing when the game moves.

An oscillator makes the tone. A gain node in front of it is the volume. Wire the first into the second, wire that into the speakers, then book two changes: where the pitch travels and how fast the volume drops to nothing.

Five moves make a sound effect A waveform, a pitch that travels, and a volume that falls to nothing. 1 Pick a waveform Square reads as retro. Sine is soft. Sawtooth bites. 2 Set a start pitch High for a coin, low for a thud. One number, by ear. 3 Slide it somewhere Up for a jump, down for a hit. The slide is the character. 4 Fade the gain down Ramp it to almost zero. A gain that stops dead clicks. 5 Stop the node Book the stop when you start it. Oscillators are one use. Under 200 milliseconds, or the thirtieth coin is the one that loses the player.
A square wave sliding upward is a jump. The same wave, higher and shorter, is a coin. Most of a sound library is one function called with different numbers.
javascriptOne function, four sounds, no files
let ctx = null;
let master = null;

// Called from a real click. Until this runs, nothing can be heard.
function startAudio() {
  if (!ctx) {
    ctx = new AudioContext();
    master = ctx.createGain();
    master.gain.value = 0.3;      // headroom, because effects overlap
    master.connect(ctx.destination);
  }
  ctx.resume();                   // safe to call again later
}

// oscillator -> envelope -> master -> speakers
function blip(type, from, to, seconds, peak = 0.6) {
  if (!ctx) return;               // no gesture yet, so nothing to do
  const t = ctx.currentTime;      // the audio clock, not Date.now()

  const osc = ctx.createOscillator();
  const env = ctx.createGain();
  osc.type = type;                // square, sine, sawtooth, triangle

  osc.frequency.setValueAtTime(from, t);
  osc.frequency.exponentialRampToValueAtTime(to, t + seconds);

  // Ramp to almost zero, never to zero: the exponential ramp needs a
  // positive target, and a hard stop is an audible click anyway.
  env.gain.setValueAtTime(peak, t);
  env.gain.exponentialRampToValueAtTime(0.0001, t + seconds);

  osc.connect(env);
  env.connect(master);
  osc.start(t);
  osc.stop(t + seconds);          // a node is used once and discarded
}

const sfx = {
  jump: () => blip('square', 180, 540, 0.12),
  coin: () => blip('square', 900, 1600, 0.09),
  hit:  () => blip('sawtooth', 320, 90, 0.15),
  over: () => blip('triangle', 440, 110, 0.6, 0.5),
};

Every time in there is a time on the context's own clock, read once at the top as currentTime. The node is created, connected, started and stopped in the same breath, and then it is rubbish. You do not keep oscillators around and replay them. You make a new one each time, which is cheaper than it sounds.

An explosion is the one that will not come from a tone, because an explosion is noise. Fill a short buffer with random numbers, play it once through the same falling gain, and you have a burst. Run it through a low-pass filter and the hiss turns into a thump.

javascriptNoise, for anything that is a crash rather than a note
function burst(seconds = 0.35, peak = 0.5) {
  if (!ctx) return;
  const t = ctx.currentTime;

  const frames = Math.floor(ctx.sampleRate * seconds);
  const buf = ctx.createBuffer(1, frames, ctx.sampleRate);
  const data = buf.getChannelData(0);
  for (let i = 0; i < frames; i++) data[i] = Math.random() * 2 - 1;

  const src = ctx.createBufferSource();
  src.buffer = buf;

  const lp = ctx.createBiquadFilter();
  lp.type = 'lowpass';
  lp.frequency.setValueAtTime(1800, t);
  lp.frequency.exponentialRampToValueAtTime(200, t + seconds);

  const env = ctx.createGain();
  env.gain.setValueAtTime(peak, t);
  env.gain.exponentialRampToValueAtTime(0.0001, t + seconds);

  src.connect(lp);
  lp.connect(env);
  env.connect(master);
  src.start(t);
}

Short, and then shorter than that#

Length is where a first attempt goes wrong once the sound exists at all. A half-second whoosh on every jump is charming the first time and unbearable by the second level. Trim the duration before you touch the pitch.

The test is not how an effect sounds once. It is how it sounds thirty times in two minutes, which is what a player who is good at your game will hear. Most of what makes an effect tiring is simply that it lasts longer than the action it belongs to.

EffectRough lengthShape to ask for
Coin or pickup60 to 100 msHigh square tone, pitch sliding up
Jump100 to 150 msMid tone rising, cut short
Hit or damage120 to 200 msLower tone falling, harsher waveform
Explosion300 to 500 msFiltered noise, volume falling away
Game over500 ms to 1 sThree falling notes, played once

Numbers to start from rather than rules. Only the game over sound has room to be slow, because the player hears it once and is not doing anything else.

Volume, and the tab mute button#

Run every sound through one gain node on its way out, and let only that node touch the speakers. It gives you one number for the whole game. It also gives you a mute that works, because setting one value to zero silences everything, including the sounds already playing.

What you are avoiding is the player reaching for the tab's own mute. That player is gone for the rest of the session, and they will not come back to turn it on. Nothing in the game can argue with them once they have done it.

  • Start quieter than feels right. You have heard your own effects hundreds of times and stopped noticing them. A level around a third of full scale leaves room for several sounds at once without clipping.
  • Put the mute where a hand already is. A corner of the screen, visible without pausing, and bound to a key as well. A mute buried in a settings panel is the tab mute with extra steps.
  • Remember the choice. Keep the mute state in localStorage and read it back on load. A player who silenced you yesterday has told you something, and asking again tomorrow is rude.
  • Mute, do not pause. Setting the master gain to zero is instant and reversible. Tearing the context down means rebuilding it, and rebuilding it means needing another gesture.

One sound per impact, not one per frame#

Hang a sound on a collision test and you have hung it on the frame loop. A player resting on a spike plate is colliding sixty times a second, so the hurt sound fires sixty times a second. Sixty copies of one tone stacked together are not sixty times louder. They are a buzz, and they clip.

The cure is to play on the change rather than on the state. Remember what was true last frame. Fire the sound only where the condition was false then and is true now. That is the moment of impact, and it happens exactly once per impact.

  • Edges, not levels. Touching counts as one event. Still touching is not a new event, however many frames it goes on for.
  • Give repeats a floor. A footstep every 300 milliseconds while a character walks, not one per frame. Store the time of the last play and compare.
  • Cap how many play at once. Twelve bullets leaving on the same frame should sound like a volley, not a wall. Play two or three and drop the rest.
  • Nudge the pitch each time. Multiply the start frequency by a small random amount. Identical repeats are most of what makes an effect grating.

Music, and whether you want any#

Music is a different trade, and for a one-file game the honest answer is often none at all. A loop is a file. A file is weight on the first load, a second request, somewhere to host it and a question about who owns it. All of that for a track the player may mute in a minute.

Generated music is the other road, and it is real but modest. A short arpeggio from the same oscillators costs no bytes and no requests. Four notes going round, a steady pulse, and a filter opening and closing will carry a menu screen or an idle game without pretending to be a soundtrack.

Anything timed to a beat is a harder problem than it looks, and it is not this article's. Notes fired from the frame loop drift apart over the length of a song, and the fix is a scheduling habit rather than a setting. The music game guide covers that clock properly, so this one will not repeat it.

Silence is a legitimate choice. Sharp effects over a quiet background beat a loop the player turns off, and no loop at all is one less thing to load.

🔊Games on this site

Common questions#

Why does my browser game make no sound?

Almost always because the audio was set up before the player touched anything. A context built during page load sits suspended, and a suspended context swallows everything sent to it without complaining. Build or resume it inside a click handler and the same code works.

Do I need sound files to add audio to a game?

No. An oscillator and a gain node give you a jump, a coin, a hit and a game over in a few lines each. A buffer of random numbers gives you an explosion. For a single-file game this is the better answer, because there is nothing extra to load and nothing to go missing.

How long should a game sound effect be?

Most of them under 200 milliseconds. A pickup can be as short as 60. Only the game over sound has room to run on, because it plays once at a moment when the player is not doing anything. Judge the length by hearing the effect thirty times, never once.

Why does one hit play the sound dozens of times?

The sound is tied to a collision being true rather than to it becoming true, so it fires on every frame of contact. Keep last frame's answer and play only on the change. A minimum gap between repeats of the same effect catches whatever that misses.

How loud should the effects be?

Quieter than you think, and all of them through one master gain node. Around a third of full scale is a sensible start, because several effects can land on the same frame and a full-scale stack will clip. The real measure is whether anyone reaches for the tab mute.

Should I add background music to a small game?

Only if you can do it without a file. A loop is bytes, hosting and ownership questions for something many players switch off. A short generated arpeggio costs nothing, and silence under good effects is a respectable finish.

Where to go next#

Sound is the cheapest thing you can do to a finished game and the one players notice fastest. When a jump sounds right, publish it. The page records the model and the prompt behind the game, so the next person can see how it was built.

The general method is in how to make a game with AI, and prompts for particular faults live in the prompt guide. The games on Agent Games are worth opening with the volume up, because you can hear within a second which ones bothered.