How to play a sound when Claude Code finishes or needs input

To play a sound when Claude Code finishes or needs you, add a Stop hook and a Notification hook to your settings.json file, and have each run an audio player on a short sound file. The sounds in this guide are CC0, so they need no credit.

Last updated:

Example tracks

These are real catalog tracks. The credit line under each row is the one Uncommon Ear generates.

Steps

  1. Save the sound files

    Pick one short sound for each event. The three examples below are the ones on the coding agents page. Save them in a folder such as ~/.claude/sounds as done.wav and needs-input.wav, and error.wav if you want a third sound for failures.

  2. Test the player command

    Before you touch any settings, run the command in your terminal. On macOS, afplay comes with the system. On Linux, use a player such as paplay or aplay instead. If you hear nothing here, Claude Code will not play it either.

    Test the player command
    afplay "$HOME/.claude/sounds/done.wav"
  3. Choose where the hooks live

    Open ~/.claude/settings.json to hear the sounds in every project. Open .claude/settings.json in a project folder to hear them in that project only, and to share them with the team if you commit the file. Use .claude/settings.local.json for a project setting that stays on your machine.

  4. Add a hook for when Claude finishes

    The Stop event fires when Claude finishes responding. Stop does not support a matcher, so the hook runs after every turn. If your file already has a hooks key, add Stop inside it instead of replacing the object.

    Add a hook for when Claude finishes
    {
      "hooks": {
        "Stop": [
          {
            "hooks": [
              {
                "type": "command",
                "command": "afplay \"$HOME/.claude/sounds/done.wav\""
              }
            ]
          }
        ]
      }
    }
  5. Add a hook for when Claude needs you

    The Notification event fires when Claude Code sends a notification. Its matcher picks the notification type. The permission_prompt type fires when Claude needs you to approve something and the prompt has waited about six seconds. The idle_prompt type fires when Claude finished about 60 seconds ago and you have not typed since.

    Add a hook for when Claude needs you
    {
      "hooks": {
        "Notification": [
          {
            "matcher": "permission_prompt|idle_prompt",
            "hooks": [
              {
                "type": "command",
                "command": "afplay \"$HOME/.claude/sounds/needs-input.wav\""
              }
            ]
          }
        ]
      }
    }
  6. Add a hook for errors

    If you want a third sound, add a StopFailure hook. It fires when a turn ends because of an API error, and its matcher filters by error type.

    Add a hook for errors
    {
      "hooks": {
        "StopFailure": [
          {
            "matcher": "rate_limit|overloaded|server_error",
            "hooks": [
              {
                "type": "command",
                "command": "afplay \"$HOME/.claude/sounds/error.wav\""
              }
            ]
          }
        ]
      }
    }
  7. Check that Claude Code loaded the hooks

    In Claude Code, enter /hooks. It opens a read-only list of your hooks and shows where each one comes from. Then ask Claude to run a command that needs permission and look away from the terminal for a few seconds.

  8. Stop a long sound from blocking Claude

    Command hooks block by default, so a long sound holds up the hook until it ends. Keep sounds under a couple of seconds, or set async to true to run the command in the background.

    Stop a long sound from blocking Claude
    {
      "hooks": {
        "Stop": [
          {
            "hooks": [
              {
                "type": "command",
                "command": "afplay \"$HOME/.claude/sounds/done.wav\"",
                "async": true
              }
            ]
          }
        ]
      }
    }

Common mistakes

  • Replacing the whole hooks object. If the file already has hooks, add your events as siblings of the existing ones.
  • Adding a matcher to Stop. The event has no matcher support, so the matcher is ignored.
  • Leaving the Notification matcher empty. An empty matcher fires on every notification type, including ones you do not need a sound for.
  • Expecting the sound on your laptop when Claude Code runs over SSH. The hook runs on the machine where Claude Code runs.
  • Using a long sound or a long track. Pick a sound well under two seconds, so you hear which event fired before the next one starts.
  • Skipping the player test. If the command fails in your terminal, the hook fails the same way.