# Error Sounds Must Not Punish

**SHOULD** · **ID:** `interactions-sound-not-punishing` · **Category:** interactions
**Source:** Custom
**Interactive version:** https://ui-guides-agent-rules.netlify.app/principles/interactions-sound-not-punishing

> SHOULD: An error sound must inform, not scold. Keep it brief, moderate in level, and gentle — typically lower and falling, distinct from a rising success tone. A harsh, loud, dissonant buzzer makes users mute the whole product, losing every useful cue with it. The worded, visible error still carries the message and the fix.

An error tone should be distinct and calm, not a harsh loud alarm — punishing sounds make users mute everything

Cherry-picked from the appropriateness rules in Raphael Salaja's sounds-on-the-web skill. An error is information, not a verdict, and the sound has to agree with that. A loud, low, dissonant buzzer does the opposite — it startles, it reads as a reprimand for a typo, and the entirely rational response is to mute the product, which also silences the confirmations you actually wanted the user to hear. It is the same escape-hatch failure that over-frequent sound causes, arrived at from the other direction: make any one sound intolerable and the user kills all of them. A good error tone is brief, moderate in level, and gentle — often pitched lower than success and falling rather than rising, so it is instantly distinguishable from a confirmation without being aggressive. As always the sound is the junior partner: the visible, worded error carries the message and the fix (this is the audio counterpart of content-actionable-errors and content-positive-language, which say the copy should guide rather than blame), and interactions-sound-not-sole-channel keeps it that way.

## Rule snippet

```tsx
// gentle, brief, downward — noticed, not punishing
playTone({ frequency: 392, endFrequency: 294, duration: 0.28, type: 'sine', volume: 0.3 });
```

## Bad — do not do this

`interactions-sound-not-punishing-bad`

```tsx
import { useState } from 'react';
import { playTone } from '@/components/demo-kit/playBlip';

/**
 * A harsh, loud, dissonant sawtooth buzzer on every validation error. It startles,
 * it feels like a reprimand, and the escape hatch users reach for is to mute ALL sound —
 * taking the useful confirmations down with it.
 */
export function SoundNotPunishingBad() {
  const [soundEnabled, setSoundEnabled] = useState(false);
  const [errored, setErrored] = useState(false);

  const triggerError = () => {
    // Loud, low, dissonant sawtooth = alarm, not information.
    if (soundEnabled) playTone({ frequency: 160, duration: 0.5, type: 'sawtooth', volume: 0.55, dissonant: true });
    setErrored(true);
  };

  return (
    <div className="w-full max-w-sm">
      <div className="rounded-lg border border-border bg-card p-4">
        <label className="mb-4 flex items-center gap-2 text-xs text-muted-foreground">
          <input
            type="checkbox"
            checked={soundEnabled}
            onChange={(e) => setSoundEnabled(e.target.checked)}
            className="size-4 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
          />
          Enable sound for this demo (off by default — it is loud)
        </label>

        <button
          type="button"
          onClick={triggerError}
          className="w-full rounded-md bg-primary px-3 py-2 text-sm text-primary-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
        >
          Submit with an invalid field
        </button>
        <p role="status" aria-live="polite" className={`mt-3 min-h-4 text-xs ${errored ? 'text-destructive' : ''}`}>
          {errored ? 'Error: enter a valid email' : ''}
        </p>
      </div>
      <p className="mt-4 text-xs text-destructive">
        A harsh, loud buzzer punishes the user for a typo — so they mute everything and lose the good cues too.
      </p>
    </div>
  );
}
```

## Good — do this

`interactions-sound-not-punishing-good`

```tsx
import { useState } from 'react';
import { playTone } from '@/components/demo-kit/playBlip';

/**
 * The same error, a soft low tone that dips gently and stops. Distinct enough to notice,
 * calm enough that no one reaches for the mute switch. The visual message still leads;
 * the sound only draws the eye to it.
 */
export function SoundNotPunishingGood() {
  const [soundEnabled, setSoundEnabled] = useState(false);
  const [errored, setErrored] = useState(false);

  const triggerError = () => {
    // Gentle, brief, a soft downward sine — noticed, not punishing.
    if (soundEnabled) playTone({ frequency: 392, endFrequency: 294, duration: 0.28, type: 'sine', volume: 0.3 });
    setErrored(true);
  };

  return (
    <div className="w-full max-w-sm">
      <div className="rounded-lg border border-border bg-card p-4">
        <label className="mb-4 flex items-center gap-2 text-xs text-muted-foreground">
          <input
            type="checkbox"
            checked={soundEnabled}
            onChange={(e) => setSoundEnabled(e.target.checked)}
            className="size-4 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
          />
          Enable sound for this demo (off by default)
        </label>

        <button
          type="button"
          onClick={triggerError}
          className="w-full rounded-md bg-primary px-3 py-2 text-sm text-primary-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
        >
          Submit with an invalid field
        </button>
        <p
          role="status"
          aria-live="polite"
          className={`mt-3 min-h-4 text-xs ${errored ? 'text-destructive' : ''}`}
        >
          {errored ? 'Error: enter a valid email' : ''}
        </p>
      </div>
      <p className="mt-4 text-xs text-success">
        A soft, brief, downward tone marks the error without alarming — users keep sound on.
      </p>
    </div>
  );
}
```

## References

- [Raphael Salaja: sounds-on-the-web SKILL.md](https://github.com/raphaelsalaja/skill/blob/main/skills/sounds-on-the-web/SKILL.md)
- [NN/g: Error-Message Guidelines](https://www.nngroup.com/articles/error-message-guidelines/)
