Skip to content

Interface SFX

An interface sound is a cue, not a soundtrack. It says one thing, that worked, and then it's gone. The shortest cue on this page lasts 16ms and the longest is under 300ms.

Each cue should match the action it confirms. A toggle gets a two-note flip, a finished task gets a short tick, a sent message gets a rising arpeggio, and a removal gets a falling pop. Try each control below.

Sync across devices
Reply to Ana
Attachmentbrief.pdf

These are the sounds this site plays, adapted from patches in Raphael Salaja's @web-kits/audio. They are synthesized in the browser, so there are no audio files to load.

A sound confirms something you can already see. It never carries the message alone. The switch moves, the text strikes through, the button reads Sent. Some people have sound off, some are on a train, and some just don't want it.

Where sound helps

Sound earns its place when an action finishes and your eyes might be elsewhere: sending, copying, saving, a long upload completing, a toggle you flipped from the keyboard.

It hurts almost everywhere else. Typing, scrolling, opening menus, moving between pages, anything that happens dozens of times a minute. If a cue can fire twice in the same second, it should probably not exist.

Keep it quiet

The most common mistake is volume. In @web-kits/audio a layer plays at a gain of 0.5 unless you set one. Raphael's Minimal patch keeps every layer between 0.04 and 0.12, and the click cues on this site sit between 0.04 and 0.1. Copy the link at each level.

linear.app/craft/issue/CRA-142

Loud is the same cue with every layer at five times the gain. It still tops out at 0.3, below the library default, and it is already the kind of sound people mute an app for.

Hover is a whisper or nothing

Hover fires constantly, so a real cue on hover turns a sidebar into a keyboard. Move across the list with each setting.

The whisper is a sine at a gain of 0.01, a quarter of what Minimal ships for hover. It rises 25 cents, a quarter of a semitone, per item, so moving down the list feels like running a finger along a rail. The tick is the click sound, and on hover it gets tiring fast.

Browsers keep audio suspended until the first click or key press, so drop hover cues before that instead of queueing them. And put a mute switch somewhere obvious. This site keeps one in the header and remembers it.

Usage

app.tsx
import { definePatch, ensureReady } from "@web-kits/audio";

const ui = definePatch({
  name: "ui",
  sounds: {
    tick: {
      source: { type: "sine", frequency: 1200 },
      envelope: { attack: 0, decay: 0.012, sustain: 0, release: 0.004 },
      gain: 0.08,
    },
  },
});

async function play(name: string) {
  try {
    await ensureReady(); // resumes the AudioContext inside the click
    ui.play(name);
  } catch {
    // No audio available. The visual change still happens.
  }
}

<Button
  onClick={() => {
    setDone(true);
    play("tick");
  }}
>
  Done
</Button>

A quick test: trigger your cue twenty times in a row. If you're tired of it by the tenth, lower the gain, shorten the decay, or cut it.

Resources

Search concepts

Search for a command to run...