@davexdev/claude-bell
v0.3.0
Published
Sound notifications for Claude Code — play a sound when a task finishes, when Claude is waiting for approval, or when a subagent completes.
Maintainers
Readme
| Sound | Plays when | |-------|------------| | ✅ complete | A task finishes (Claude stops responding) | | ⏳ waiting | Claude is waiting for your approval to run a tool | | 💤 idle | Claude is idle, waiting for your next prompt | | 🤖 subagent | A subagent finishes | | ❌ error | A terminal command fails (optional — off by default) |
It works by registering Claude Code hooks that run a tiny, dependency-free Node script to play a sound. Cross-platform: Windows, macOS, Linux.
Six sound themes are bundled — pick whichever fits your taste:
| Theme | Feel |
|-------|------|
| mac (default) | Polished bell/chime tones — warm, inharmonic partials that ring out naturally, like a system notification. |
| retro | Chiptune pulse/triangle tones — classic game-console voices, punchy and playful. |
| glass | Minimal, near-inaudible taps — filtered-noise clicks with no tone at all, for when even a chime is too much. |
| arcade | Synthwave pad stabs — detuned analog-style oscillators with a filter-sweep brightening, warm 80s retro-futurism (not chiptune). |
| marimba | Acoustic mallet strikes — a noise transient plus inharmonic wooden partials, the most "real instrument" of the bunch. |
| hud | FM-synthesized sci-fi blips — metallic, evolving tones with glitchy static on errors, straight off a starship console. |
npx @davexdev/claude-bell theme retro # switch
npx @davexdev/claude-bell test # hear itRequirements
- Node.js 16 or newer (
node --versionto check) - Claude Code installed
- Linux only: an audio player — one of
paplay(pulseaudio-utils),aplay(alsa-utils),ffplay(ffmpeg), orplay(sox). macOS and Windows work out of the box.
Quick start
# 1. Register the sound hooks (global — applies to every project)
npx @davexdev/claude-bell install
# 2. Check your speakers work
npx @davexdev/claude-bell test
# 3. Start a NEW Claude Code session — that's it 🎉⚠️ Hooks load when a session starts. After installing, open a new Claude Code session; already-open sessions won't play sounds until restarted.
Installation
Option A — no install (recommended to start)
npx @davexdev/claude-bell installnpx downloads and runs it on the fly — nothing stays installed globally.
Option B — install globally (shorter command)
npm install -g @davexdev/claude-bell
claude-bell installAfter a global install the command is just claude-bell.
What install does
- Writes the hooks into your user settings:
~/.claude/settings.json(so every Claude Code session notifies you). - Backs up your settings first (
settings.json.bak-<timestamp>). - Only adds claude-bell's entries — your existing settings and hooks are untouched.
- Is safe to run again (re-running replaces only claude-bell's entries, never duplicates).
Install options
| Command | Effect |
|---------|--------|
| install | Global — writes to ~/.claude/settings.json (all projects). |
| install --project | This project only — writes to ./.claude/settings.json. |
| install --errors | Also play a sound when a terminal (Bash) command fails. Off by default (see below). |
You can combine flags, e.g. install --project --errors.
Verify it works
- Open a new Claude Code session.
- Ask it something small (e.g. "list the files here").
- When it finishes → you should hear the complete sound. 🔔
Not hearing anything? See Troubleshooting.
The error sound (optional)
By default there is no sound for failures, because tool failures are common
and would be noisy (a grep with no matches, a failing test — all exit non-zero).
Turn it on explicitly:
npx @davexdev/claude-bell install --errorsThis adds a PostToolUseFailure hook scoped to Bash, so you get an audible
cue only when a terminal command fails. To turn it back off, run uninstall
then install again without --errors.
Configuration
Everything is optional — the defaults just work. To customize:
npx @davexdev/claude-bell configThis creates ~/.claude/claude-bell.config.json (and prints your current settings). Open it in any editor and change what you like:
{
"enabled": true,
"theme": "mac",
"sounds": {
"complete": "complete.wav",
"waiting": "waiting.wav",
"idle": "idle.wav",
"subagent": "subagent.wav",
"error": "error.wav"
},
"events": {
"Stop": "complete",
"Notification.permission_prompt": "waiting",
"Notification.idle_prompt": "idle",
"Notification.agent_completed": "complete",
"SubagentStop": "subagent",
"PostToolUseFailure": "error"
}
}theme= which bundled sound pack to use for bare filenames — one ofmac,retro,glass,arcade,marimba,hud(see the table above). Ignored for absolute paths. Switch it withclaude-bell theme <name>.sounds= named sounds → a file (a bundled.wavname looked up in the current theme, or an absolute path to your own).events= a Claude Code event → the name of the sound to play.
Your config is merged over the defaults, so you only need to include the keys you want to change. Changes apply in the next Claude Code session (
testreflects them immediately).
Recipes — "I want to…"
| Goal | How |
|------|-----|
| Switch the whole vibe | claude-bell theme retro (or edit "theme" in your config). |
| Use my own sound for task-done | Set "complete": "C:/Users/me/Music/tada.wav" (Windows) or "/home/me/tada.wav" (macOS/Linux) under sounds. Use an absolute path. |
| Silence one situation (e.g. subagents) | Delete its line from events (remove "SubagentStop": ...). |
| Change which sound an event uses | Repoint it, e.g. "Stop": "idle" to play the soft tone when a task finishes. |
| Mute everything temporarily | Set "enabled": false. |
| Same sound for all notifications | Add a single "Notification": "waiting" key (used as a fallback when a specific Notification.* key isn't set). |
Event keys
| Key | Fires when |
|-----|------------|
| Stop | Claude finishes responding (task done) |
| Notification.permission_prompt | Claude asks to run a tool and needs approval |
| Notification.idle_prompt | Claude is idle, waiting for your next prompt |
| Notification.agent_completed | A background session finished or failed |
| SubagentStop | A subagent (Task) finished |
| PostToolUseFailure | A Bash command failed (only active with install --errors) |
Command reference
| Command | What it does |
|---------|--------------|
| install [--project] [--errors] | Register the hooks (global by default). |
| uninstall [--project] | Remove only claude-bell's hooks; everything else stays. |
| test [sound] [--theme=name] | Play a sound now to check audio. Names: complete, waiting, idle, subagent, error. Default: complete. --theme previews a theme without saving it. |
| theme [name] | Show the current/available themes, or switch to one (mac, retro, glass, arcade, marimba, hud). |
| config | Create/print your user config file. |
| help | Show usage. |
| version | Print the version. |
(Prefix with npx @davexdev/claude-bell if you didn't install globally.)
Troubleshooting
No sound at all in a Claude Code session? Walk through this:
- Did you start a new session after installing? Hooks load at session start.
- Does
testmake a sound?npx @davexdev/claude-bell test- No sound from
test→ it's a system audio issue (volume/output device), or on Linux you're missing a player (see Requirements). testworks but sessions are silent → enable the debug log (next step).
- No sound from
- Turn on the debug log to see whether the hook is firing. Set
"debug": truein~/.claude/claude-bell.config.json, start a new session, do a task, then check:
Each event appends a line like:~/.claude/claude-bell.log2026-01-01T00:00:00.000Z event=Stop file=.../complete.wav played=true- A line with
played=true→ it's working (turndebugback tofalse). - A line with
played=false→ the sound file couldn't be played (bad path, or missing Linux player). - No new lines → Claude Code isn't running the hook. Re-run
installand confirm you started a fresh session.
- A line with
How it works
install writes hook entries into your settings.json:
{
"hooks": {
"Stop": [
{ "matcher": "", "hooks": [
{ "type": "command", "command": "\"node\" \".../src/play.js\" Stop" }
] }
]
}
}On each event, Claude Code runs play.js, which reads the event JSON on stdin,
looks up the mapped sound in your config, and plays it with the OS-native player:
- Windows — PowerShell
System.Media.SoundPlayer - macOS —
afplay - Linux — the first available of
paplay,aplay,ffplay,play
Playback is synchronous (the hook waits the ~0.5–1s it takes to play the short
sound) so Claude Code's hook runner doesn't reap the audio process early. Any error
is swallowed and the process exits 0, so the notifier can never break Claude Code.
Limitations
- No sound for in-line errors. Claude Code has no dedicated hook for general
errors.
install --errorscovers Bash command failures (viaPostToolUseFailure); other kinds of failures aren't signaled. - Volume isn't adjustable on Windows (
SoundPlayerhas no volume control) — use quieter source files if needed. - Linux needs one of the audio players listed in Requirements.
Development
git clone https://github.com/DavexDev/claude-bell.git
cd claude-bell
npm link # exposes the `claude-bell` command locally
npm test # smoke tests (no audio, no settings changes)
npm run gen-sounds # regenerate the bundled WAV filesThe bundled sounds are synthesized tones — royalty-free, no third-party audio.
Each theme's synthesis lives in scripts/sound-themes/<name>.js (shared DSP
helpers in scripts/sound-themes/synth.js); scripts/gen-sounds.js renders
them all to sounds/<theme>/*.wav.
