SFXMint

Add sound effects to a Phaser web game

Free, CC0, no API key. Pick a kit, preview it in your game, preload its files and play cues on game events, loop an ambience bed. Works in Phaser 3 with the default Web Audio manager.

Quickest path: npx sfxmint add --set platformer downloads checked MP3 files (or WAV or OGG with --format) and a manifest (source URL, SHA-256, license) into your game folder; then preload them as in step 2.

By · Published · Updated

1. Pick a set

A set groups candidates by event and style; listen to them together to judge whether they fit.platformer covers jump, land, coin, hit, win, 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. Try the platformer set in its demo scene before you wire it up. All 18 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 MP3 urls map. Request format=wavor format=mp3 to use download_urls for that format. Checkmissing_roles and content_check; unverified previews are not accepted content. This illustrative map is not a current recommendation:

"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.

Cues that repeat quickly (coins in a row, hits in a combo) can overlap, but a pile of copies gets loud and muddy. Let a few play at once and skip the rest:

// At most three copies of one cue at a time; the next one waits for a copy to finish.
cue(key, config) {
  const playing = this.sound.getAll(key).filter((s) => s.isPlaying).length;
  if (playing < 3) this.sound.play(key, config);
}

Mute and volume

Give players a mute key and remember it. this.sound.mute and this.sound.volume act on every sound of the game at once. Keep the mute state in your own variable: with Web Audio, reading this.sound.mute right after setting it can still return the old value, and the wrong state gets saved.

create() {
  this.sound.volume = 0.8;
  let muted = localStorage.getItem("sfx-muted") === "1";
  this.sound.mute = muted;
  this.input.keyboard.on("keydown-M", () => {
    muted = !muted;
    this.sound.mute = muted;
    localStorage.setItem("sfx-muted", muted ? "1" : "0");
  });
}

4. Background ambience

SFXMint does not mark any file as a seamless loop, so there is no loop role to ask for. Pick an ambience file with search, use the WAV (MP3 adds a short gap at the join), and listen for the seam in your own build before you ship it on a loop.

curl "https://sfxmint.com/api/v1/search?q=forest+ambience&response=structured&limit=3"
# → { "candidates": [{ "slug": "...", "wav_url": "https://sfxmint.com/dl/<slug>.wav", ... }], ... }
preload() {
  this.load.audio("ambience", "<wav_url of the file you picked>");
}

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

6. One-off sounds: roles and search

curl "https://sfxmint.com/api/v1/roles/chest-open"            # File-checked candidate + 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&response=structured&limit=3"

Roles organize candidates by purpose, with aliases listed at /roles. All match types and score describe retrieval or ranking, not listening confidence. Pass required max_duration_ms, loop andformat; inspect structured checks, and judge closest sounds by their missing_terms. Do not treat near_matches as meeting hard conditions. Acoustic measurements help compare variants but do not prove that a sound fits the event. Preview the actual game.

If the task permits trimming or level changes, preserve originals, source and license, record the processing recipe and output hashes, and re-check the delivered result. Changed bytes do not inherit the original content review. Other permitted sources or local synthesis can be used when they fit better.

7. Sounds made for your game: AI Studio

When the sets don't cover your game, AI Studio takes the list of sounds your game needs, uses free library sounds first and makes the rest.Its download is one ZIP: the files, a README and sounds.json, whose events map each event key to its file (null for a sound not made yet). Unzip it so sounds.json sits in public/sfx/, then load the map and its files together:

preload() {
  this.load.json("sfx-map", "sfx/sounds.json");
  // Files can be added while the loader runs: each event's file loads under the event's key.
  this.load.on("filecomplete-json-sfx-map", (_key, _type, map) => {
    for (const [event, entry] of Object.entries(map.events)) {
      if (entry) this.load.audio(event, "sfx/" + entry.file);
    }
  });
}

// An event whose sound is not made yet stays silent instead of throwing.
cue(event, config) {
  if (!this.cache.audio.exists(event)) return;
  const playing = this.sound.getAll(event).filter((s) => s.isPlaying).length;
  if (playing < 3) this.sound.play(event, config);
}

Keep sounds.json with the files: it records each file's SHA-256, its source and the take you picked, and the license is CC0 like the library. A coding assistant can do all of this through the MCP Game kit tools.

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 — a role for one event, a set for a project, search for a description or a gap:

npx skills add flreey/sfxmint-mcp -y

MCP: claude mcp add --transport http sfxmint https://sfxmint.com/mcp exposes get_role_sound, get_sound_set and search_sounds. Full reference: API docs.