SFXMint

Add sound effects to a React app

Try your choices in a scene: UI feedback demo · Video transition demo.

Free, CC0, no API key. Fetch a set of candidates, preview them in your app and connect them with a small play() module. No account is required.

Quickest path: npx sfxmint add --set saas-app downloads checked MP3 files (or WAV or OGG with --format) and a manifest (source URL, SHA-256, license) into your project. The package also ships a small runtime that unlocks audio on the first tap, lets cues overlap and remembers mute.

By · Published · Updated

1. Fetch a sound set

Sets group candidates by role and measured acoustics. Listen to the selected cues together to judge whether they fit your product. For a work tool try 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 18are listed at /sets.

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

The illustrative response below uses the legacy MP3 urls map. For a required format, request format=wav|mp3|ogg and use download_urls. Check missing_roles and content status before integration.

{
  "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/ready-success-01.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? Select an alternate and use its returned URL; do not invent file URLs.

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/ready-success-01.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 a file-checked candidate with alternates; preview it in the app to judge suitability:

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&response=structured&limit=3"

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. Every match type and score describes retrieval or ranking, not listening confidence. Pass required duration, loop and format options, inspect structuredchecks, judge closest sounds by their missing_terms, and keep near_matches separate. File checks and unverified content status do not certify suitability; preview cues in the app. The role index is at /roles.

Choose a role for a named event, a set for several events, or search for a description. If the task permits processing, keep originals, source and license, record the recipe and new hashes, and re-check the processed result. A changed file does not inherit its original content review. Other permitted sources or local synthesis remain options.

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 so your agent can choose a role for a named event, a set for several events, or search for a description or a gap, alongside 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.