Add sound effects to a Phaser web game
Free, CC0, no API key. Pick one coherent kit, preload it straight from the URLs, play cues on game events, loop an ambience bed. Works in Phaser 3 with the default Web Audio manager.
1. Pick a set
A set is one sound per role, chosen so the kit sounds like one game. platformer covers jump, land, coin, hit, sword, gunshot, explosion, footsteps, door, checkpoint and death; retro-game is the chiptune version; puzzle-game has match, combo, bonus, win and lose; horror-game has drone, stinger, heartbeat and creak. All 15 sets are at /sets.
curl "https://sfxmint.com/api/v1/sets/platformer"The response has a sounds object per role (slug, acoustics, two alternates) and a flat urls map you can paste as-is:
"urls": {
"jump": "https://sfxmint.com/dl/retro-game-jump-06.mp3",
"land": "https://sfxmint.com/dl/impact-thud-33.mp3",
"coin": "https://sfxmint.com/dl/retro-game-coin-13.mp3",
"hit": "https://sfxmint.com/dl/impact-punch-25.mp3",
"sword": "https://sfxmint.com/dl/impact-sword-02.mp3",
"gunshot": "https://sfxmint.com/dl/impact-gunshot-01.mp3",
"explosion": "https://sfxmint.com/dl/retro-game-explosion-25.mp3",
"footsteps": "https://sfxmint.com/dl/footsteps-wood-18.mp3",
"door": "https://sfxmint.com/dl/door-open-25.mp3",
"checkpoint": "https://sfxmint.com/dl/feedback-confirm-24.mp3",
"death": "https://sfxmint.com/dl/retro-game-game-over-24.mp3"
}Picks can change as the library grows; the URLs you paste never do.
2. Preload
Loop over the map in preload(). Absolute URLs are fine — no setBaseURL, no setCORS needed.
// sfx.js — paste the "urls" map from the set response
export const SFX = {
jump: "https://sfxmint.com/dl/retro-game-jump-06.mp3",
coin: "https://sfxmint.com/dl/retro-game-coin-13.mp3",
hit: "https://sfxmint.com/dl/impact-punch-25.mp3",
checkpoint: "https://sfxmint.com/dl/feedback-confirm-24.mp3",
death: "https://sfxmint.com/dl/retro-game-game-over-24.mp3",
};
// GameScene.js
import { SFX } from "./sfx";
export class GameScene extends Phaser.Scene {
preload() {
for (const [key, url] of Object.entries(SFX)) this.load.audio(key, url);
}
}3. Play on game events
create() {
this.physics.add.overlap(this.player, this.coins, (_player, coin) => {
coin.destroy();
this.sound.play("coin", { volume: 0.6 });
});
}
update() {
if (this.cursors.up.isDown && this.player.body.blocked.down) {
this.player.setVelocityY(-330);
this.sound.play("jump", { volume: 0.5 });
}
}this.sound.play(key, config) creates a throwaway sound each time, which is right for short cues. For something you trigger dozens of times a second (footsteps, machine-gun fire) keep one instance with this.sound.add(key) and call .play() on it.
4. Background ambience on a loop
Ask for a loop role — rain-loop, forest-loop, wind-loop, drone-loop, engine-loop — and you get a sound with loopable: true. Use its wav_url: the WAV is gapless at the seam, the MP3 has a few milliseconds of codec padding you will hear as a tick.
curl "https://sfxmint.com/api/v1/roles/forest-loop"
# → { "role": "forest-loop", "loopable": true, "wav_url": "https://sfxmint.com/dl/<slug>.wav", ... }preload() {
this.load.audio("ambience", "<wav_url from the response>");
}
create() {
const startAmbience = () => this.sound.add("ambience", { loop: true, volume: 0.3 }).play();
if (this.sound.locked) {
this.sound.once(Phaser.Sound.Events.UNLOCKED, startAmbience);
} else {
startAmbience();
}
}this.sound.locked is true until the player has clicked or tapped — browsers refuse to start audio before a gesture. Cues you play from input handlers are already inside a gesture; the ambience is the one thing that starts on its own, so it waits for Phaser.Sound.Events.UNLOCKED.
5. Why hotlinking just works
- Phaser's Web Audio manager fetches audio with XHR and decodes it, so a cross-origin file needs CORS. SFXMint sends
Access-Control-Allow-Origin: *on every/dl/URL. The HTML5 Audio fallback needs nothing. Cache-Control: immutable: the browser keeps the files for a year; repeat visits load nothing. URLs are permanent — a slug is never reused or re-encoded.- MP3 for size (a 1 s cue is about 15 KB), WAV when you need gapless loops or lossless quality. Both are always available for every sound.
- Shipping to itch.io, Steam or an offline build? Copy the files into
assets/sfx/and point the map at relative paths. CC0 allows it. Never append query parameters to/dl/URLs.
6. One-off sounds: roles and search
curl "https://sfxmint.com/api/v1/roles/chest-open" # QA-checked default + alternates
curl "https://sfxmint.com/api/v1/roles/laser?style=crisp" # re-rank the family by acoustics
curl "https://sfxmint.com/api/v1/search?q=crate+breaking&category=impact&limit=5"Roles are the reliable path: one deterministic default per purpose, aliases accepted, listed at /roles. Search results carry match — role and exact are safe to ship blind, partial and semantic deserve a listen. Every result includes acoustics (attack, tail, brightness) so you can pick the punchier or darker variant without opening a DAW.
License
Every SFXMint sound is CC0 1.0: commercial use, no attribution, no signup, no credits screen required. Full terms at /license.
Building with an AI agent?
Claude Code, Cursor and similar agents can install the skill and follow this workflow on their own — role first, then set, then search — instead of inventing URLs:
npx skills add flreey/sfxmint-mcp -yMCP: claude mcp add --transport http sfxmint https://sfxmint.com/mcp exposes get_role_sound, get_sound_set and search_sounds. Full reference: API docs.