Design the sound for the thirtieth play

How I replaced the recording sounds in a Chrome extension, and why the plainest of four modelled instruments won.

9 min read

A compact cassette recording in a tape deck. Blank tape runs from the full supply reel past the record head, which writes the signal onto it, to the take-up reel. The take-up reel turns faster because its pack is smaller. Supply reel Magnetic tape Record head Take-up reel Capstan Pinch roller 123 456
A sound is recorded once and played back hundreds of times.

Every recording in the extension I work on at my company starts with a sound and ends with one. People hear the pair dozens of times a day. Both sounds came from ElevenLabs' sound effects generator, and they sounded it.

The old pair didn't sound like a pair

Old

two bare sine tones, G5 → D6

G4 and G5 under hiss, then a tick

a mono start, a wide stereo stop

Every waveform on this page is drawn from its own file, on one shared time scale, with level in decibels from −60 dB at the centre line. Files play at their real levels, as Opus where the browser supports it and AAC where it doesn't.

The start is 0.31 s and mono: two sine tones a fifth apart, with nothing above them louder than −50 dB. The stop is 0.81 s and stereo: a G an octave lower, under a hiss that runs to the end of the file. In its last 10 ms the right channel jumps about 30 dB, and the file ends mid-swing.

Whether they even match in level depends on where you listen. Through two speakers they measure −18.3 and −18.5 LUFS. Fold them to mono, as a single earbud or a mono accessibility setting does, and the stop drops to −23.3, 5 dB under the start. Its two channels only partly agree, with a correlation of 0.33, so in mono they partly cancel.

More from the same generator sounded the same

My first fix was more ElevenLabs. I prompted for "minimal app UI stop" sounds until two were decent.

ElevenLabs, stop only

152 ms of tick, 650 ms of silence

47 ms of nothing before it starts

Neither was right. Number 2 is a 152 ms tick padded out with silence. Number 3 waits 47 ms before it makes a sound, then comes in about 4 dB louder than the old stop. And neither had a start to go with it.

Both files were exactly 0.8065 s and stereo, the same as the old stop. That's how I recognised the old stop as ElevenLabs too. A generator pours every result into the same container, and nothing in that makes two sounds belong together.

A sound heard thirty times a day has rules

So I read how sound designers approach interface sound, and kept the rules that apply to a cue people hear all day.

  • Use instruments, not tones. Stephen Brewster's earcon guidelines recommend musical instrument timbres with several harmonics, which are easier to perceive and harder to mask. Keep pitch between about 150 Hz and 5 kHz. Accent the first note, let the last one run slightly longer, and keep it short. He also names intensity as the main cause of annoyance.
  • Make a pair a pair. Material Design's sound choreography says sounds that follow each other in a flow should share attributes like timbre, melody or envelope, and that an "on" sound can answer its "off" sound with a motif that reverses direction. Applying sound to UI adds that a frequent sound has to survive being played often.
  • Round it. Microsoft described the Windows 11 sounds as having a much rounder wavelength, softer but still able to alert you, the way the interface got rounded corners. In dark mode they also echo a little.
  • Never let sound carry the message alone. Apple's Playing audio guidance treats sound effects as nonessential: silent mode mutes them and system volume governs them. Its Feedback guidance pairs sound with color, text and haptics, so people get the message with the sound off.
  • Keep it short and level. Two sound-library blogs, which I treat as secondary, suggest 200 to 500 ms with true peaks between −1 and −3 dB, and rarely more than 500 ms.

One more rule comes from the ear itself. Hearing is most sensitive between about 2 and 5 kHz, where the ear canal resonates. Energy there sounds louder than a meter says, and it's where a short sound turns harsh.

Model an object, not a beep

I built four candidate pairs: tine, wood, glass and pluck. None is a synth preset. Each is a small physical model of something you could strike or pluck.

A struck bar is a handful of sine waves, called partials, each at the bar's own ratio to the note and each fading at its own rate, plus a few milliseconds of filtered noise for the mallet. The ratios are what make wood sound like wood. A marimba bar is carved underneath so its first overtone lands two octaves up, on the fourth harmonic, and the next near the tenth. My wood bar sits close to that:

# (frequency ratio, level, decay)
WOOD = [(1, 1.0, 0.7), (3.99, 0.32, 0.2), (9.1, 0.08, 0.06)]

The tine's first overtone sits at 5.4 times the note, between two harmonics, which is why it rings like metal. Glass has four partials, at 1, 2.32, 4.25 and 6.63 times. The pluck is a Karplus–Strong string: a burst of noise circling a short delay line and averaged on every pass, so it settles into the full harmonic series and darkens as it fades.

Overtones of one note, by frequency

ears most sensitive
Old
784 Hz at 0 dB, 2355 Hz at -53 dB
Tine
879 Hz at 0 dB, 4752 Hz at -22 dB
Wood
881 Hz at 0 dB, 3507 Hz at -18 dB, 8015 Hz at -42 dB
Glass
987 Hz at 0 dB, 2292 Hz at -12 dB, 4199 Hz at -25 dB, 6545 Hz at -38 dB
Pluck
880 Hz at 0 dB, 1760 Hz at 0 dB, 2640 Hz at -6 dB, 3520 Hz at -4 dB, 4400 Hz at -5 dB, 5280 Hz at -7 dB, 6160 Hz at -10 dB, 7040 Hz at -10 dB, 7919 Hz at -10 dB, 8799 Hz at -19 dB
The overtones in the first 40 ms of one note from each pair, measured from the files with an FFT. Each stem's height is its level against the note itself, from −60 dB at the baseline. The old start gives the ear one frequency to hold on to. The pluck gives it ten.

Round the edges, then roughen them

A model played perfectly sounds like a machine. Each partial gets up to 2.6 cents of random detune, and each note up to 4 ms of timing drift, because a player is never quite on the grid. Apple does something similar in visionOS, where the system subtly varies the pitch and volume of keyboard sounds so they don't turn repetitive. The random seed is fixed, so every build of a file comes out the same.

Then the rounding. A 220 ms small-room reverb, mixed in at 22 percent, puts the bar somewhere instead of nowhere. A high-pass at 160 Hz sits on Brewster's floor, and a 2.5 dB dip around 3.5 kHz takes the edge off the band where ears are sharpest. On the wood bar that dip lands on the 4× overtone of its top note. Each note's decay is set to fall about 35 dB before the file ends, and linear fades taper the last 50 ms, so nothing is cut off mid-ring. Every file starts making sound within 3.4 ms and peaks at −4 dBFS, a decibel under the blogs' range, which leaves room for the encoder.

The notes tie each pair together. Every start rises and its stop falls back through the same interval: a major third for tine, a fourth for wood, a fifth for pluck. Glass plays one note each way, its stop a fifth below its start.

Tine

A5 → C♯6, a major third up

C♯6 → A5, back down

a metal tine: its overtone sits between harmonics, so it shimmers

Wood

E5 → A5, a fourth up

A5 → E5, back down

a rosewood bar, tuned like a marimba: overtones at 4× and 9.1×

Glass

one soft tap on B5

a fifth lower, on E5

no melody, four partials, the longest ring

Pluck

D5 → A5, a fifth up

A5 → D5, back down

a Karplus–Strong string: every harmonic, darkening as it fades

The four candidates at their real levels. All of them peak at −4 dBFS, yet their loudness differs by up to 5.8 dB.

Equal peaks aren't equal loudness

Tine's stop measures −15.1 LUFS and pluck's −20.9, from the same −4 dBFS peak. A peak is one sample. Loudness is energy over time, weighted toward the frequencies ears favour, and a long ring carries more of it than a quick pluck. Played side by side, the louder sound tends to win for being louder.

So I built a small listening page. It loudness-matches every file to −23 LUFS, the EBU R 128 broadcast target. It plays the start, then a gap standing in for the recording, then the stop. The number keys map to the pairs, so I could play one ten times in a row without reaching for the mouse.

The prettiest one lost on repeat

It came down to tine and wood. Tine was the most blissful of the four, a small kalimba melody. Wood was plainer. I tried to keep tine's tone with two compromises: tine-short, at wood's length, and tine-soft, with a quiet octave partial added and the metal overtone 16 dB down.

Tine-short

tine's tone at wood's length

Tine-soft

an octave added, the metal ring 16 dB quieter

I still picked wood, for two reasons.

The first is repetition. A melody that's lovely on the first play is still a melody on the thirtieth, and by then it's asking for attention it hasn't earned. Wood's knock says "started" and gets out of the way.

The second is time. The extension waits for the start cue to finish before capture feels live, with a 2 s timeout, so every millisecond of start cue is added to every recording.

From click to recording, in seconds

Old0.31 sTine0.41 sGlass0.44 sPluck0.42 sTine-short0.29 sWood0.28 s
Each start cue drawn from its file on one time axis, from the click to the moment capture goes live. The extension waits for the cue to finish, up to 2 s, so its length is added to every recording.

Wood's start is 0.28 s and tine's is 0.41. Tine-short closed that gap and still lost on repetition, which told me repetition was the real reason.

What shipped

Woodshipped

−18.9 LUFS, 5 KB

−18.9 LUFS, 7 KB

the stop turned down 1.9 dB to meet the start

The shipped files are mono Opus. I turned the stop down 1.9 dB so both measure −18.9 LUFS, which makes them a pair in level as well as in timbre. Here is every pair on one scale.

Loudness, LUFS

Old, stereoStart -18.3 LUFS, stop -18.5 LUFS0.2 dBOld, monoStart -18.3 LUFS, stop -23.3 LUFS5.0 dBTineStart -16.1 LUFS, stop -15.1 LUFS1.0 dBWoodStart -18.8 LUFS, stop -17.0 LUFS1.8 dBGlassStart -18.0 LUFS, stop -16.1 LUFS1.9 dBPluckStart -20.7 LUFS, stop -20.9 LUFS0.2 dBShippedStart -18.9 LUFS, stop -18.9 LUFS0.0 dB
Loudness of each pair, start against stop. My own reading, taken in Python: K-weighted audio, the loudest 400 ms window, channels summed, following ITU-R BS.1770 and EBU momentary loudness. Close to LUFS, not a certified meter.

A sound people hear thirty times a day should be designed for the thirtieth time. The first listen picks the prettiest sound, and the thirtieth picks the one that gets out of the way.