sounds-on-the-web

Original🇺🇸 English
Translated

Audit UI code for audio feedback best practices. Use when reviewing sound implementation, checking audio UX decisions, or auditing accessibility. Outputs file:line findings.

9installs
Added on

NPX Install

npx skill4agent add raphaelsalaja/userinterface-wiki sounds-on-the-web

Sounds on the Web

Review UI code for audio feedback best practices and accessibility.

How It Works

  1. Read the specified files (or prompt user for files/pattern)
  2. Check against all rules below
  3. Output findings in
    file:line
    format

Rule Categories

PriorityCategoryPrefix
1Accessibility
a11y-
2Appropriateness
appropriate-
3Implementation
impl-
4Weight Matching
weight-

Rules

Accessibility Rules

a11y-visual-equivalent

Every audio cue must have a visual equivalent; sound never replaces visual feedback.
Fail:
tsx
function SubmitButton({ onClick }) {
  const handleClick = () => {
    playSound("success");
    onClick(); // No visual confirmation
  };
}
Pass:
tsx
function SubmitButton({ onClick }) {
  const [status, setStatus] = useState("idle");
  
  const handleClick = () => {
    playSound("success");
    setStatus("success"); // Visual feedback too
    onClick();
  };
  
  return <button data-status={status}>Submit</button>;
}

a11y-toggle-setting

Provide explicit toggle to disable sounds in settings.
Fail:
tsx
// No way to disable sounds
function App() {
  return <SoundProvider>{children}</SoundProvider>;
}
Pass:
tsx
function App() {
  const { soundEnabled } = usePreferences();
  return (
    <SoundProvider enabled={soundEnabled}>
      {children}
    </SoundProvider>
  );
}

a11y-reduced-motion-check

Respect prefers-reduced-motion as proxy for sound sensitivity.
Fail:
tsx
function playSound(name: string) {
  audio.play(); // Plays regardless of preferences
}
Pass:
tsx
function playSound(name: string) {
  const prefersReducedMotion = window.matchMedia(
    "(prefers-reduced-motion: reduce)"
  ).matches;
  
  if (prefersReducedMotion) return;
  audio.play();
}

a11y-volume-control

Allow volume adjustment independent of system volume.
Fail:
tsx
function playSound() {
  audio.volume = 1; // Always full volume
  audio.play();
}
Pass:
tsx
function playSound() {
  const { volume } = usePreferences();
  audio.volume = volume; // User-controlled
  audio.play();
}

Appropriateness Rules

appropriate-no-high-frequency

Do not add sound to high-frequency interactions (typing, keyboard navigation).
Fail:
tsx
function Input({ onChange }) {
  const handleChange = (e) => {
    playSound("keystroke"); // Annoying on every keystroke
    onChange(e);
  };
}
Pass:
tsx
function Input({ onChange }) {
  // No sound on typing - visual feedback only
  return <input onChange={onChange} />;
}

appropriate-confirmations-only

Sound is appropriate for confirmations: payments, uploads, form submissions.
Pass:
tsx
async function handlePayment() {
  await processPayment();
  playSound("success"); // Appropriate - significant action
  showConfirmation();
}

appropriate-errors-warnings

Sound is appropriate for errors and warnings that can't be overlooked.
Pass:
tsx
function handleError(error: Error) {
  playSound("error"); // Appropriate - needs attention
  showErrorToast(error.message);
}

appropriate-no-decorative

Do not add sound to decorative moments with no informational value.
Fail:
tsx
function Card({ onHover }) {
  return (
    <div onMouseEnter={() => playSound("hover")}> {/* Decorative, no value */}
      {children}
    </div>
  );
}

appropriate-no-punishing

Sound should inform, not punish; avoid harsh sounds for user mistakes.
Fail:
tsx
function ValidationError() {
  playSound("loud-buzzer"); // Punishing
  return <span>Invalid input</span>;
}
Pass:
tsx
function ValidationError() {
  playSound("gentle-alert"); // Informative but not harsh
  return <span>Invalid input</span>;
}

Implementation Rules

impl-preload-audio

Preload audio files to avoid playback delay.
Fail:
tsx
function playSound(name: string) {
  const audio = new Audio(`/sounds/${name}.mp3`); // Loads on demand
  audio.play();
}
Pass:
tsx
const sounds = {
  success: new Audio("/sounds/success.mp3"),
  error: new Audio("/sounds/error.mp3"),
};

// Preload on app init
Object.values(sounds).forEach(audio => audio.load());

function playSound(name: keyof typeof sounds) {
  sounds[name].currentTime = 0;
  sounds[name].play();
}

impl-default-subtle

Default volume should be subtle, not loud.
Fail:
tsx
const DEFAULT_VOLUME = 1.0; // Too loud
Pass:
tsx
const DEFAULT_VOLUME = 0.3; // Subtle default

impl-reset-current-time

Reset audio currentTime before replay to allow rapid triggering.
Fail:
tsx
function playSound() {
  audio.play(); // Won't replay if already playing
}
Pass:
tsx
function playSound() {
  audio.currentTime = 0;
  audio.play();
}

Weight Matching Rules

weight-match-action

Sound weight should match action importance.
Fail:
tsx
// Loud fanfare for minor action
function handleToggle() {
  playSound("triumphant-fanfare");
  setEnabled(!enabled);
}
Pass:
tsx
// Subtle click for minor action
function handleToggle() {
  playSound("soft-click");
  setEnabled(!enabled);
}

// Richer sound for significant action
function handlePurchase() {
  playSound("success-chime");
  completePurchase();
}

weight-duration-matches-action

Sound duration should match action duration.
Fail:
tsx
// 2-second sound for instant action
function handleClick() {
  playSound("long-whoosh"); // 2000ms
  // Action completes immediately
}
Pass:
tsx
// Short sound for instant action
function handleClick() {
  playSound("click"); // 50ms
}

// Longer sound for process
function handleUpload() {
  playSound("upload-progress"); // Matches upload duration
}

Output Format

When reviewing files, output findings as:
file:line - [rule-id] description of issue

Example:
components/input/index.tsx:23 - [appropriate-no-high-frequency] Playing sound on every keystroke
lib/sounds.ts:45 - [a11y-reduced-motion-check] Not checking prefers-reduced-motion

Summary Table

After findings, output a summary:
RuleCountSeverity
a11y-visual-equivalent
2HIGH
appropriate-no-high-frequency
1HIGH
impl-preload-audio
3MEDIUM

Sound Appropriateness Matrix

InteractionSound?Reason
Payment successYesSignificant confirmation
Form submissionYesUser needs assurance
Error stateYesCan't be overlooked
NotificationYesMay not be looking at screen
Button clickMaybeOnly for significant buttons
TypingNoToo frequent
HoverNoDecorative only
ScrollNoToo frequent
NavigationNoKeyboard nav would be noisy

References