Build Wispr Flow
YESreplaces $15/mosaves $180/yrback to the verdict
System-wide dictation for your Mac: hold a key to record, release to transcribe locally with whisper.cpp, run the text through a quick LLM cleanup, and insert it at the cursor in whatever app has focus. A floating pill shows it is live. Works offline with the local model; no accounts, no telemetry.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- have readythe Mac you have
Why Local transcription latency is only acceptable on Apple Silicon.
Get it Apple menu > About This Mac.
Verify
sw_vers prints 14 or higher - installfree
Why Swift and the macOS SDK.
Get it Mac App Store; open once to install components. open ↗
Verify
xcodebuild -version prints 16 or higher - installfree
Why Dictation is judged on the wait. small.en answers a short clip in well under three seconds; medium roughly doubles it.
Get it git clone https://github.com/ggml-org/whisper.cpp && cd whisper.cpp && cmake -B build && cmake --build build --config Release && ./models/download-ggml-model.sh small.en open ↗
Verify
./build/bin/whisper-cli -h prints usage and models/ggml-small.en.bin exists (about 500 MB) - API keypay per use; fractions of a cent per dictation
Why Removes filler words and fixes punctuation. Without it the raw transcript is inserted, which is still useful.
Get it Anthropic: console.anthropic.com > API Keys. OpenAI: platform.openai.com/api-keys. Into .env; never in source. open ↗
- API keyfree tier available
Why A faster path on slower machines. Local stays the default.
Get it console.groq.com > API Keys. open ↗
- decidefree
Why Input Monitoring lets the app observe the global hotkey; Accessibility lets it post the synthetic paste. Confusing them costs an afternoon. Each prompt appears once per launch; if dismissed, relaunch.
Get it System Settings > Privacy & Security > Input Monitoring, and > Accessibility. Add the built app to both when prompted.
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
WHISPER_BIN | required | /Users/you/whisper.cpp/build/bin/whisper-cli | Path to the binary you built. |
WHISPER_MODEL | required | /Users/you/whisper.cpp/models/ggml-small.en.bin | small.en for latency; medium.en if accuracy matters more. |
HOTKEY | required | fn | The hold-to-talk key. fn, right-option, or a key combination. |
ANTHROPIC_API_KEYsecret | optional | sk-ant-... | Anthropic console. Empty disables the cleanup pass. |
LLM_MODEL | required | claude-haiku-4-5-20251001 | A fast, cheap model is right for a cleanup pass. |
GROQ_API_KEYsecret | optional | gsk_... | Enables hosted transcription when TRANSCRIBE_MODE=hosted. |
TRANSCRIBE_MODE | required | local | local or hosted. |
RESTORE_CLIPBOARD | required | false | true restores your previous clipboard after inserting; false leaves the transcript on it. |
The build, in order
Hotkey and recording
Hold to record, release to stop, with a WAV whisper accepts, plus hands-free, a hard cap and Esc to cancel.
Prove capture in the CLI target first.
Files
Dictate.xcodeprojRequires Input Monitoring. Detect key down and key up for HOTKEY. Detect a double-tap for hands-free.
Tap the input node; on release stop and write 16 kHz mono 16-bit PCM WAV via AVAudioConverter. Not 44.1 kHz, not float.
Observe Esc passively so it still reaches the app being typed in.
- terminal
afinfo /tmp/clip.wav $WHISPER_BIN -m $WHISPER_MODEL -f /tmp/clip.wav
done when · tick each as it passeswatch out- The permission prompt appears once per launch. If the user dismisses it, the app must be relaunched before it can ask again; detect the denied state and say so.
Transcription
Under three seconds from key release to text for a short clip, local by default.
Key release to transcript, in the console, so regressions are visible.
done when · tick each as it passesCleanup pass
Filler removed, punctuation fixed, and never silence when the model fails.
Return only the cleaned text, no preamble. Strip a leading 'Here is' defensively.
done when · tick each as it passesInsertion
Text appears at the cursor in any app, with the clipboard handled the way the user chose.
Requires Accessibility: check AXIsProcessTrustedWithOptions and guide the user to the pane. Per-character CGEvent typing needs key-code mapping and breaks on layouts; the paste approach is the default.
Save the previous pasteboard contents, paste, then restore after a short delay when the flag is true.
done when · tick each as it passeswatch out- Password fields refuse synthetic input by design. Say so in the README; it is not a bug to fix.
Feedback UI
You always know whether it is listening.
A small always-on-top panel that never takes focus, showing recording, transcribing or cleaning, the mode, and a live level meter.
On/off, launch at login, settings, recent transcripts.
done when · tick each as it passesSettings, offline, README
Configurable, works offline, documented.
Both permissions and their panes, the relaunch caveat, the password-field limitation, model sizes and latency.
Files
README.md
done when · tick each as it passesDistribute itproduct builder
Notarized, self-updating, with opt-in crash reports.
- terminal
xcrun notarytool submit Dictate.zip --keychain-profile notary --wait xcrun stapler staple Dictate.app
done when · tick each as it passes
That is the whole plan for Wispr Flow. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Their tuned auto-editing voice model and per-app tone formatting.
- The mobile keyboard.
- Polish on accents, noise and crosstalk where hosted models lead.
- their tuned auto-editing voice model
- per-app tone formatting
- the mobile keyboard
- polish on edge cases (accents, noise)
- Custom vocabulary passed to the cleanup prompt
- Per-app cleanup styles (terse in a terminal, prose in mail)
Need the files? The project pack on the verdict page hands your agent the whole brief · more dictation.