Build Wispr Flow

YESreplaces $15/mosaves $180/yrback to the verdict

0%0 of 23 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

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.

estimated effort weekendthe files for this build are in the project pack

AppSwift 6, SwiftUI, MenuBarExtra, macOS 14+AudioAVAudioEngineTranscriptionwhisper.cpp small.en via whisper-cliCleanupAn LLM API behind an interface, optionalInsertionPasteboard plus a synthetic Cmd-V

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. 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

  2. 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

  3. 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)

  4. 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 ↗

  5. API keyfree tier available

    Why A faster path on slower machines. Local stays the default.

    Get it console.groq.com > API Keys. open ↗

  6. 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.

VariableNeededExampleWhere the value comes from
WHISPER_BINrequired/Users/you/whisper.cpp/build/bin/whisper-cliPath to the binary you built.
WHISPER_MODELrequired/Users/you/whisper.cpp/models/ggml-small.en.binsmall.en for latency; medium.en if accuracy matters more.
HOTKEYrequiredfnThe hold-to-talk key. fn, right-option, or a key combination.
ANTHROPIC_API_KEYsecretoptionalsk-ant-...Anthropic console. Empty disables the cleanup pass.
LLM_MODELrequiredclaude-haiku-4-5-20251001A fast, cheap model is right for a cleanup pass.
GROQ_API_KEYsecretoptionalgsk_...Enables hosted transcription when TRANSCRIBE_MODE=hosted.
TRANSCRIBE_MODErequiredlocallocal or hosted.
RESTORE_CLIPBOARDrequiredfalsetrue restores your previous clipboard after inserting; false leaves the transcript on it.

The build, in order

  1. Hotkey and recording

    Hold to record, release to stop, with a WAV whisper accepts, plus hands-free, a hard cap and Esc to cancel.

    1. Prove capture in the CLI target first.

      Files Dictate.xcodeproj

    2. Requires Input Monitoring. Detect key down and key up for HOTKEY. Detect a double-tap for hands-free.

    3. Tap the input node; on release stop and write 16 kHz mono 16-bit PCM WAV via AVAudioConverter. Not 44.1 kHz, not float.

    4. Observe Esc passively so it still reaches the app being typed in.

    5. terminal
      afinfo /tmp/clip.wav
      $WHISPER_BIN -m $WHISPER_MODEL -f /tmp/clip.wav
    done when · tick each as it passes
    watch 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.
  2. Transcription

    Under three seconds from key release to text for a short clip, local by default.

    1. Key release to transcript, in the console, so regressions are visible.

    done when · tick each as it passes
  3. Cleanup pass

    Filler removed, punctuation fixed, and never silence when the model fails.

    1. Return only the cleaned text, no preamble. Strip a leading 'Here is' defensively.

    done when · tick each as it passes
  4. Insertion

    Text appears at the cursor in any app, with the clipboard handled the way the user chose.

    1. 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.

    2. Save the previous pasteboard contents, paste, then restore after a short delay when the flag is true.

    done when · tick each as it passes
    watch out
    • Password fields refuse synthetic input by design. Say so in the README; it is not a bug to fix.
  5. Feedback UI

    You always know whether it is listening.

    1. A small always-on-top panel that never takes focus, showing recording, transcribing or cleaning, the mode, and a live level meter.

    2. On/off, launch at login, settings, recent transcripts.

    done when · tick each as it passes
  6. Settings, offline, README

    Configurable, works offline, documented.

    1. 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 passes
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more dictation.