Every sound in the kit, next to every action it could serve. Swap, trim, listen, then paste the result into packages/core/src/uiSounds.ts.
Four sounds, four moments. These buttons call the real playUiSound(), so they are rate-limited exactly as in the app — a second click inside the interval does nothing.
| Action | Sound | Gain | Level | |
|---|---|---|---|---|
Answer graded correct correct both answer docks · both rapid fires | notification.wav | 0.5 | — | |
Answer graded partial partial session-v2/AnswerDock.tsx (web) · session/AnswerDock.tsx (mobile) | select.wav | 1 | — | |
Answer graded incorrect incorrect both answer docks · both rapid fires | transition_down.wav | 0.5 | — | |
Exercise finished exerciseComplete both session pages, on the sectionComplete SSE meta | celebration.wav | 1 | — |
Deliberately silent
Not rate-limited here — hammer one sound against another. Level is the file's baked loudness plus your gain, so two rows showing the same number will feel equally loud.
packages/core/src/uiSounds.tsexport const UI_SOUNDS: Record<UiSoundName, UiSoundSpec> = {
correct: { file: 'notification.wav', gain: 0.5, minIntervalMs: 120 },
partial: { file: 'select.wav', gain: 1, minIntervalMs: 120 },
incorrect: { file: 'transition_down.wav', gain: 0.5, minIntervalMs: 120 },
exerciseComplete: { file: 'celebration.wav', gain: 1, minIntervalMs: 2000 },
};Moving a file into the shipped set also means adding it to WANTED in scripts/build-ui-sounds.js, re-running it, and adding a require() line in mobile/src/lib/uiSounds.ts — otherwise web plays it and mobile silently cannot.
All 28 files, baked to the same loudness as the shipped set, so a comparison here is a fair one.