Dr Jim Kennedy  ·  Applied Leverage

WhisperBar

Free, private, offline Mac dictation. One hotkey. Works in any app. 170 lines of Python — built with Claude from a spec.

This page is the blueprint. Below is the full PRD that Claude used to build WhisperBar — reverse-engineered from the finished app so every requirement is real, nothing is aspirational, and there are zero TBDs.

The code is on GitHub. Take either, or both, and build your own version.


The one sentence the whole build hangs on

WhisperBar is a signal chain: sound goes in one end, and text comes out the other.

⌥Space → Mic → Buffer → Whisper → Clipboard → ⌘V → Cursor

Everything else is wrapping around that line — a menu-bar icon to show you what's happening, and a hotkey to set it off. If you understand the chain, you already understand the build plan.


Decision log

Every non-obvious choice, with the trade-off that made it. A good spec doesn't just say what — it says why. This is the part most build videos skip.

  1. UI style

    Menu-bar app, not a floating window

    Zero footprint, always-on. Cost: the icon state has to carry all the feedback.

  2. Trigger model

    Toggle — press once to start, once to stop

    Allows long dictation without holding a key. Cost: you must remember you're recording — the 🔴 mitigates it.

  3. Text insertion

    Clipboard + simulated ⌘V

    Works in literally every app via the OS paste path. Cost: clobbers the clipboard; needs Accessibility permission.

  4. Transcription

    Local Whisper, on-device

    Private, offline, no API cost or key. Cost: uses local CPU.

  5. Default model

    base

    Best everyday speed/accuracy balance — feels near-instant. small+ are more accurate but add seconds.

  6. Precision flag

    fp16=False in model.transcribe()

    Forces CPU-friendly fp32 for Apple Silicon. Avoids fp16-on-CPU warnings without needing a GPU.

  7. Paste timing

    150ms sleep before ⌘V

    Gives focus time to return to the previous app before pasting. Tunable upward on slower machines.

  8. Language

    Hard-pinned to English

    Skips auto-detect for speed and consistency. Trade-off: English only as shipped — easy to remove.

  9. Persistence

    launchd agent + launch.sh wrapper

    Always-on with no terminal. Cost: a small piece of macOS plumbing outside the Python app.

  10. Config

    Three settings in config.py, not hardcoded

    Things you want to change are in one obvious place. That's the difference between a script and a tool.


What you need

8 Python packages

Install once with pip install. They map directly to stations in the signal chain.

Plus one non-Python install: ffmpeg via Homebrew — Whisper's audio decoder.

3 macOS permissions

System Settings → Privacy & Security. Grant all three before first run.

First-run gotcha: if you see "This process is not trusted — input event monitoring will not be possible", the host process (Terminal or python3) is missing from Input Monitoring and/or Accessibility. Add it to both, quit, and relaunch.

The build: 5 stations

Built in signal-chain order — each station is independently testable and adds one link to the chain.

  1. 1
    Audio capturesounddevice fires a callback continuously — each chunk drops into a buffer while recording is true. Start recording empties the bucket, flips the flag on, and opens the mic. One station, one job.
  2. 2
    Transcribe + pasteStop the mic, write a temp WAV, hand it to Whisper, copy the text, sleep 150ms, simulate ⌘V, delete the temp file. The finally block runs even if something goes wrong — that's what stops the app getting stuck.
  3. 3
    Menu-bar UIA rumps.App subclass. Three icon states (🎙 idle / 🔴 recording / ⏳ transcribing) do all the UI work. toggle() is the brain — 5 lines, two branches.
  4. 4
    Global hotkeyA pynput listener on a daemon thread maintains a set of currently-held keys. When ⌥Space matches, it calls toggle() — from any app, even when WhisperBar isn't in front.
  5. 5
    Wire up + auto-startLoad Whisper once at startup (slow — do it once, not per dictation). Start the hotkey listener on a daemon thread. Hand control to app.run(). A launchd agent starts everything at login.

Why the spec is the moat

Most build videos show you the code. The code is the commodity — Claude can write 170 lines of Python in minutes. What it can't do is know that you want toggle instead of hold-to-talk, or that you care about the clipboard gotcha, or that the launchd agent is the detail that turns a weekend script into a daily tool.

That's the 90/500 Method: your judgment writes the spec (your 90), Claude's knowledge of code executes it (the 500). You stay the architect. The PRD above is what your 90 looks like — written down, precise, and shareable.

Take a process from your own domain and try to write a spec for it. You'll understand your own workflow better than you did before you started — and you'll have something Claude can actually build from.

The code is not the important part

WhisperBar is evidence of a larger shift: when execution becomes cheaper, the valuable contribution is choosing the right problem and specifying what good looks like.

Your version does not need to be an app. It could be a service, a workflow, a guide, or simply a better way to do work that matters to you.

— Dr Jim Kennedy

Get The Signal

One useful idea each week about what AI is changing — and how to make your experience count for more.

No spam. One email a week.