Dictee is my own dictation app. I press Caps Lock, speak, press Caps Lock again, and the cleaned text lands where my cursor is. It's also on the clipboard. Nothing leaves the Mac: speech recognition, cleanup and storage all run locally.
The name is the French dictée, and it fits the -ee family of my other tools.

Why I built it
I liked Wispr Flow for its output. It still sends every dictation to its cloud, even with Privacy Mode on, and it runs on a subscription. I wanted 3 things it couldn't give me:
- Local and private. Client names and deal details never leave the machine.
- Accurate in my 3 languages. English, French and German, with my names and Kaltura terms spelled right.
- Flexible. My hotkey, my formatting rules, my dictionary, my apps.
I compared the options first (VoiceInk, Handy, superwhisper and others, see macOS Voice Dictation Solutions), then decided to build my own.
What it does
| Feature | How it works |
|---|---|
| Caps Lock trigger | Press to start, press to stop. Shift+Caps records privately, Esc cancels |
| Live transcript | The panel shows my words as I speak, refreshed every second |
| Timer and meter | A running counter and an input level bar, so I know the mic hears me |
| Own logo and name | "Dictee" and my edelweiss logo on the panel, the menu bar and notifications |
| 1 sentence per line | Every sentence starts on a new line. Terminals get 1 line, so nothing runs by accident |
| Voice commands | "New line", "nouvelle ligne", "neuer Absatz" and so on, in EN, FR and DE |
| Cleanup | Punctuation, filler words, self-corrections, digits instead of words, my spellings |
| Dictionary | Terms and their mishearings. "Nick" becomes "Nic", "culture a" becomes "Kaltura" |
| Paste and clipboard | Pastes into the app I was in, with that app's own Paste shortcut, and keeps a copy |
| Music pause | If Audirvana is playing, Dictee pauses it and resumes it when the recording ends |
| Clear errors | A red panel plus a macOS notification that says what failed and what to do |
| Menu bar status | Logo in white when idle, red while recording, orange while transcribing, "!" on a problem |

The live transcript is raw recognition. The pasted text is the cleaned version, 1 sentence per line:

A few details I care about:
- Guards on the cleanup model. If the model changes the language or rewrites too much, Dictee pastes the raw text and tells me why. It never "improves" my meaning.
- It learns from corrections. I fix a pasted text, copy it, and save it as a correction from the menu bar. The corrections become my personal test set, and
dictee.py evalscores speech engines on my own voice. - Error messages are written for me. "The microphone sent pure digital silence. Check the microphone permission for Hammerspoon." beats a stack trace.
For business people
Dictation saves the most time on the writing I do all day: emails, Teams messages, notes after calls, prompts for Claude.
The cloud tools are good, but they send every word to someone else's server. For a seller, that means client names, deal sizes and internal plans. Dictee gives me the same workflow with none of that exposure, no subscription, and full control over how the text comes out.
The trade-off: I maintain it myself. It runs on 1 machine, my Mac Studio, and it relies on the tools already installed there.
For technical people
Architecture
Caps Lock ──> Hammerspoon (Lua)
- Caps Lock tap, overlay panel, live text, menu bar, notifications
- Audirvana pause / resume (AppleScript)
- paste with the app's own shortcut + clipboard
│ HTTP on 127.0.0.1
▼
Dictee daemon (Python)
1. record External Microphone (Rode VideoMic on the jack), 48 kHz
2. trim silence cut against the room's noise floor, resample to 16 kHz
3. ASR Parakeet TDT 0.6B v3 on MLX (mlx-audio), model kept warm
4. preview every second: re-transcribe the recording so far, no LLM
5. aliases dictionary replacements from SQLite
6. cleanup mistral-small in Ollama, temperature 0, closest dictionary terms in the prompt
7. guards language check + drift check, else keep the raw text
8. format voice commands, 1 sentence per line, no em-dashes, per-app profiles
9. log SQLite: raw text, final text, timings. Audio kept 180 days
| Part | Choice | Why |
|---|---|---|
| Speech model | NVIDIA Parakeet TDT 0.6B v3 | 25 European languages, 0.2-0.3 s for a 9 s clip |
| Runtime | mlx-audio on Apple Silicon | 1 API for Parakeet, Whisper and Voxtral |
| Cleanup model | mistral-small (24B) in Ollama | Only local model in my test that fixed names and digits without translating |
| Hotkeys and UI | Hammerspoon | Already running, reads the accessibility tree, draws overlays |
| Storage | SQLite | Dictations, corrections, dictionary |
A 9-second dictation takes about 2 seconds from stop to paste: 0.2-0.3 s for speech recognition, about 1.8 s for cleanup. Voxtral Realtime 4B scored better on French and German in published benchmarks, but took 2 s per clip on my M1 Ultra, so it stays a 1-line config switch.




Why not my own voice model
My fine-tuned model (see Fine-tuning your own model) stays out of the dictation path. It's trained to turn a brief into my writing, so it rewrites. Dictation needs the opposite: change as little as possible. It also takes about 13 seconds per text, and cleanup has to take about 1.
Gotchas I hit
- A launchd agent can't use the microphone. macOS silently hands a background Python process pure zeros, with no permission prompt. Hammerspoon starts the daemon instead, and the daemon inherits Hammerspoon's microphone permission.
- Remapping Caps Lock failed twice. A
hidutilremap from Caps Lock to F18 produced no key event at all, and my old Hyperkey setup had Caps Lock set to "No Action" in System Settings. Dictee now listens to Caps Lock itself and turns the lock straight back off. - macOS ignores quick Caps Lock taps. A built-in delay drops short presses that would turn Caps Lock on. Since Dictee resets the lock after each press, every press is an "on" press, so I sometimes had to press twice. Setting
CapsLockDelayOverrideto 0 fixed it. - Menu-based paste isn't reliable. In TextMate, choosing Edit > Paste only opened the menu. Dictee now presses each app's own Paste shortcut, read from the app's menu or from my custom shortcuts (Cmd+F is Paste on my Macs).
- Parakeet invents words from silence. Room noise alone produced "Yeah." The live preview only runs when the recording holds real speech.
How I got there: 22 prompts
I built Dictee in 1 session with Claude Code, over 2 days. I wrote 22 prompts. Claude wrote the code, tested each part on the machine, and pushed every change to a private GitHub repo.
| Phase | Prompts | What happened |
|---|---|---|
| Research | 1 | Local alternatives to Wispr Flow, pros, cons, my setup's constraints |
| Architecture | 1 | Name, requirements, full design, the voice model question |
| First build | 1 | Daemon, Hammerspoon module, dictionary, tests, repo |
| Caps key | 8 | Key loggers, raw HID monitor, Hyperkey removed, System Settings fix |
| Features and fixes | 9 | Live transcript, dictionary, errors, Cmd+F paste, logo, double press, TextMate, Audirvana |
| Questions | 2 | Where the database is, how many prompts |
The Caps key took 8 of the 22 prompts. The code was right early on. The cause sat in 2 places Claude could only diagnose: an old Hyperkey setting in System Settings and a macOS timing rule. Claude couldn't press the key or click Allow, so each test needed me at the keyboard.
What worked well:
- Start with research, then architecture, then build. The first 2 prompts produced a design I could judge before any code existed.
- Measure, don't guess. The engine and the cleanup model came from tests on my Mac, not from blog benchmarks. 1 candidate model translated my English into French.
- Ask for features in plain words. "Pause Audirvana while I record" became a feature, a test and a commit in 1 prompt.
Today it's about 1,850 lines of Python and Lua, 12 commits and 16 unit tests. It replaces Wispr Flow on my Mac Studio.
Further reading
- macOS Voice Dictation Solutions: the dictation apps I compared first
- Fine-tuning your own model: the voice model and why it stays out of this path
- The NicAI voice stack: the rest of the NicAI voice stack
- Wispr Flow, the cloud app Dictee replaces
- Audirvana, the music player Dictee pauses
- Kinesis Advantage360, the keyboard with the Caps key I use