Skip to content

Instantly share code, notes, and snippets.

@abhagsain
Created July 28, 2025 01:00
Show Gist options
  • Select an option

  • Save abhagsain/7fa112780034a5341368d87bf2bacbb1 to your computer and use it in GitHub Desktop.

Select an option

Save abhagsain/7fa112780034a5341368d87bf2bacbb1 to your computer and use it in GitHub Desktop.
Play notification sound when Claude Code Agent has finished working.

Claude Code has introduced a new feature called hooks which lets you run a script during different events. One of the event is Stop, which runs when the main Claude Code agent has finished responding.

We can make the use of this hook to play a notification sound.

You can download this notification sound (downloads automatically) from mixkit

Paste this JSON into .claude/settings.json, either project or global .claude folder. Make sure to update the path of the script.

{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/play-sound.sh"
          }
        ]
      }
    ]
  }
}

Here's a shell script which plays the sound play-sound.sh.

#!/bin/bash

SOUND_TYPE=$1
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

# Define sound files
COMPLETE_SOUND="./done.wav"
SOUND_FILE=$COMPLETE_SOUND

# Play sound based on OS
if [[ "$OSTYPE" == "darwin"* ]]; then
    # macOS
    afplay "$SOUND_FILE" 2>/dev/null &
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
    # Linux
    if command -v aplay &> /dev/null; then
        aplay "$SOUND_FILE" 2>/dev/null &
    elif command -v paplay &> /dev/null; then
        paplay "$SOUND_FILE" 2>/dev/null &
    fi
elif [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "cygwin" ]] || [[ "$OSTYPE" == "win32" ]]; then
    # Windows
    powershell -c "(New-Object Media.SoundPlayer '$SOUND_FILE').PlaySync()" 2>/dev/null &
fi

Make sure to update the permission with chmod +x play-sound.sh.


That's it. Now whenver Claude is done, it would play a sound :)

Cheers 🥂

@CtrlAltFocus
Copy link

Nice!!!

I updated the script so that i can use play-sound.sh stop and notification and subagentstop.

#!/bin/bash

# Check if an argument was provided
if [[ -z "$1" ]]; then
    echo "error: no sound type provided."
    echo "usage: $0 {stop|subagentstop|notification}"
    exit 1
fi

SOUND_TYPE=$1
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
SOUND_FILE=""

# Determine the sound file to play based on the argument
case "$SOUND_TYPE" in
    stop)
        SOUND_FILE="$SCRIPT_DIR/stop.wav"
        ;;
    subagentstop)
        SOUND_FILE="$SCRIPT_DIR/subagent-stop.wav"
        ;;
    notification)
        SOUND_FILE="$SCRIPT_DIR/notification.wav"
        ;;
    *)
        echo "error: invalid sound type '$SOUND_TYPE'."
        echo "usage: $0 {stop|subagentstop|notification}"
        exit 1
        ;;
esac

# Check if the sound file actually exists before trying to play it
if [[ ! -f "$SOUND_FILE" ]]; then
    echo "error: sound file not found: $SOUND_FILE"
    exit 1
fi

# Play sound based on OS
if [[ "$OSTYPE" == "darwin"* ]]; then
    # macOS
    afplay "$SOUND_FILE" &>/dev/null &
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
    # Linux
    if command -v aplay &> /dev/null; then
        aplay "$SOUND_FILE" &>/dev/null &
    elif command -v paplay &> /dev/null; then
        paplay "$SOUND_FILE" &>/dev/null &
    fi
elif [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "cygwin" ]] || [[ "$OSTYPE" == "win32" ]]; then
    # Windows
    # Using .Play() for asynchronous playback, similar to how & works on other OSs.
    powershell -c "(New-Object Media.SoundPlayer '$SOUND_FILE').Play()" &>/dev/null &
fi

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment