SFXMint

Add sound effects to a React app

Free, CC0, no API key. Fetch one coherent set of sounds, paste its URL map into a tiny play() module, hotlink the audio. Ten minutes, no build step, no accounts.

1. Fetch a sound set

A product needs a handful of cues that sound like they belong together, not one click. Sets give you exactly that: one sound per role, picked by measured acoustics. For a work tool use saas-app; for a consumer or chat app chat-app or ui-soft; for a store checkout; for an AI agent or CLI ai-coding-tool. All 15 are listed at /sets.

curl "https://sfxmint.com/api/v1/sets/saas-app"

The response (trimmed) — the part you want is urls:

{
  "id": "saas-app",
  "style": "crisp",
  "license": "CC0-1.0",
  "sounds": {
    "click": {
      "slug": "ui-click-30",
      "duration_ms": 1000,
      "acoustics": { "attack_ms": 0, "tail_ms": 40, "centroid_hz": 4759, "character": ["bright", "punchy", "tight"] },
      "mp3_url": "https://sfxmint.com/dl/ui-click-30.mp3",
      "wav_url": "https://sfxmint.com/dl/ui-click-30.wav",
      "alternates": ["ui-click-10", "ui-click-27"]
    }
  },
  "urls": {
    "click": "https://sfxmint.com/dl/ui-click-30.mp3",
    "hover": "https://sfxmint.com/dl/ui-hover-02.mp3",
    "toggle": "https://sfxmint.com/dl/ui-toggle-09.mp3",
    "save": "https://sfxmint.com/dl/feedback-success-22.mp3",
    "error": "https://sfxmint.com/dl/feedback-error-34.mp3",
    "notification": "https://sfxmint.com/dl/ui-chime-14.mp3"
  }
}

Picks can change as the library grows; the URLs you paste never do. Prefer a different variant? Every role lists two alternates — swap the slug.

2. A zero-dependency play() module

One module, one Audio element per sound, cached after the first play. No library needed for UI cues.

// src/sfx.ts — paste the "urls" map from the set response
const URLS = {
  click: "https://sfxmint.com/dl/ui-click-30.mp3",
  save: "https://sfxmint.com/dl/feedback-success-22.mp3",
  error: "https://sfxmint.com/dl/feedback-error-34.mp3",
  notification: "https://sfxmint.com/dl/ui-chime-14.mp3",
} as const;

export type Sfx = keyof typeof URLS;
const cache = new Map<Sfx, HTMLAudioElement>();
let muted = false;

export function setMuted(m: boolean) {
  muted = m;
}

export function play(name: Sfx, volume = 0.5) {
  if (muted || typeof window === "undefined") return; // SSR-safe
  let a = cache.get(name);
  if (!a) {
    a = new Audio(URLS[name]);
    a.preload = "auto";
    cache.set(name, a);
  }
  a.currentTime = 0; // retrigger cleanly on rapid clicks
  a.volume = volume;
  a.play().catch(() => {}); // rejected until the first user gesture — that's fine
}
import { play } from "./sfx";

export function SaveButton({ onSave }: { onSave: () => Promise<void> }) {
  return (
    <button
      onClick={async () => {
        play("click");
        try {
          await onSave();
          play("save");
        } catch {
          play("error");
        }
      }}
    >
      Save
    </button>
  );
}

3. Already using use-sound or Howler?

Same URLs, one line each. Both work with cross-origin files because the audio is served with CORS *.

import useSound from "use-sound";

const [playClick] = useSound("https://sfxmint.com/dl/ui-click-30.mp3", { volume: 0.5 });
<button onClick={() => playClick()}>Save</button>
import { Howl } from "howler";

const click = new Howl({ src: ["https://sfxmint.com/dl/ui-click-30.mp3"], volume: 0.5 });
click.play();

4. Hotlinking: what you can rely on

5. One-off sounds: roles and search

Need a cue that is not in your set? Ask by role — what the sound is for — and you get one QA-checked default instead of a text-search near miss:

curl "https://sfxmint.com/api/v1/roles/purchase-success"
# → { "role": "purchase-success", "slug": "...", "mp3_url": "...", "wav_url": "...",
#     "loopable": false, "acoustics": {...}, "alternates": [ { ...same shape } ], "family_url": "..." }

curl "https://sfxmint.com/api/v1/roles"          # every role with aliases
curl "https://sfxmint.com/api/v1/search?q=camera+shutter&limit=5"   # free text

Aliases work (button-click, ka-ching style phrases map to the same role); add ?style=crisp|soft|spacious to re-rank the family by measured acoustics. Search results carry match: role and exact are reliable, semantic is the nearest thing by meaning — listen before shipping those. The human-readable index is at /roles.

6. UX rules that keep users from muting your app

License

Every SFXMint sound is CC0 1.0: commercial use, no attribution, no signup. Full terms at /license.

Using Claude Code or Cursor?

Install the skill and your agent learns this exact workflow — role first, then set, then search — plus the autoplay and volume rules:

npx skills add flreey/sfxmint-mcp -y

Prefer MCP? claude mcp add --transport http sfxmint https://sfxmint.com/mcp gives the agent get_role_sound, get_sound_set and search_sounds natively. Full reference: API docs.