Local-first, system-wide push-to-talk dictation for macOS (Apple Silicon).
Hold a hotkey, speak, release — your words are transcribed on-device and pasted into whatever app is focused (Slack, VS Code, browser, anywhere). Transcription and the AI cleanup run locally on the Apple GPU via MLX, so your audio never leaves your machine and it works with wifi off. Voca learns your custom vocabulary and corrections, so it gets better over time.
Built and tested on a MacBook Air (Apple M4).
large-v3-turbo stays warm in memory; ~1–2s per dictation.Requires macOS on Apple Silicon (M1–M4). ~3.4 GB disk for the models (speech + on-device AI cleanup).
Voca.dmg from the latest release..dmg and drag Voca into your Applications folder.On first run:
A 🎙️ icon appears in the menu bar and Voca starts automatically at login.
Note: updating an unsigned app can reset its Accessibility permission. If the hotkey stops responding after an update, re-enable Voca under Accessibility.
Menu bar states: … loading · 🎙️ ready · 🔴 recording · ⏳ transcribing / cleaning up · ⚠️ error.
Click the 🎙️ icon:
Config lives at ~/Library/Application Support/Voca/config.json:
| Key | Default | Meaning |
|---|---|---|
hotkey | right option | Hold key. Also: left option, right/left command, right/left control, right/left shift |
model | mlx-community/whisper-large-v3-turbo | STT model (set to mlx-community/whisper-large-v3 for higher accuracy, slower) |
restore_clipboard | true | Restore your clipboard after pasting |
paste_method | paste | paste (Cmd+V) or type (keystrokes) |
mute_system_audio | true | Mute your Mac's audio while recording so the mic doesn't capture music/video |
boost_quiet_speech | true | Auto-amplify soft/low-volume voices before transcription |
streaming | false | false = transcribe the whole clip on release (accuracy-first). true = near-real-time, phrase-by-phrase while you hold |
cleanup_backend | local | AI cleanup: local (free, on-device), cloud (Anthropic, your own key), or off (rule-based punctuation only) |
cleanup_model_local | mlx-community/Qwen2.5-3B-Instruct-4bit | On-device cleanup model (downloaded once) |
cleanup_model_cloud | claude-haiku-4-5-20251001 | Model used only when cleanup_backend is cloud |
anthropic_api_key | "" | Only for cloud; the ANTHROPIC_API_KEY env var takes precedence |
Changes apply after you quit and reopen Voca (from the menu bar icon).
By default, cleanup runs on-device with a small local model — free, offline, and
private. If you prefer the highest quality and don't mind it being a paid, online
service, set cleanup_backend to cloud and provide your own Anthropic API key
(via the ANTHROPIC_API_KEY environment variable or the anthropic_api_key config
key). In cloud mode, your transcribed text (never your audio) is sent to Anthropic
for cleanup. Cloud is off unless you explicitly enable it. Set cleanup_backend
to off to skip AI cleanup entirely and use the lightweight rule-based punctuation.
Your vocabulary and corrections are stored in
~/Library/Application Support/Voca/voca.sqlite.
Voca.dmg and replace the app in Applications.git clone https://github.com/DulenW/voca.git && cd voca
python3.12 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python scripts/setup_models.py # download models (~3.7 GB, one time)
python scripts/install.py # run as a login-item LaunchAgent
scripts/uninstall.py removes the LaunchAgent; python main.py runs it once
without installing.
Build the app + .dmg:
pip install -r requirements-dev.txt
bash scripts/build_app.sh # -> dist/Voca.app and dist/Voca-1.4.0.dmg
⚠️ icon. Accessibility isn't granted. Enable
Voca under System Settings → Privacy & Security → Accessibility, then quit
and reopen Voca. (After an app update you may need to re-enable it.)hotkey in the config.CGEventTap on the main run loop waits for the hotkey.
No polling, no open mic — ~0% CPU.All transcription, AI cleanup, and pasting run on a single background worker
thread, so the menu bar and Quit always stay responsive. Custom vocabulary is
passed to Whisper as an initial_prompt (and to the cleanup model) to bias
spelling.
off, the rule-based punctuation model has a 512 word-piece limit (~350–400
spoken words); words beyond that still appear but won't get added
commas/periods. AI cleanup (the default) doesn't have this limit. Either way,
dictating in normal sentence-sized bursts works best.| File | Role |
|---|---|
main.py | Menu bar app (rumps); wires everything together |
audio.py | Mic capture (16 kHz mono float32) + live phrase chunking |
chunker.py | Splits a live stream into phrases (streaming mode) |
transcribe.py | mlx-whisper wrapper; quiet-boost, loads once, offline |
speech.py | Speech gate (energy + Silero VAD) — skips silence/noise |
enhance.py | AI cleanup + smart formatting (local MLX / cloud / off) |
punctuate.py | Rule-based English punctuation (used when cleanup is off) |
formatting.py | Capitalization, end marks, spacing |
corrections.py | SQLite learning layer (vocab + corrections) |
inject.py | Clipboard + paste via Quartz — platform-specific |
hotkey.py | Push-to-talk CGEventTap — platform-specific |
sysaudio.py | Mute system output while recording — platform-specific |
config.py | Load/save JSON config |
firstrun.py | First-launch model download |
uninstall.py | "Uninstall Voca…" — remove models + app (to Trash) |
setup.py | py2app build config for Voca.app |
scripts/build_app.sh | Build Voca.app + .dmg |
scripts/setup_models.py | One-time model download (source install) |
scripts/install.py | Install/reload the LaunchAgent (source install) |
scripts/uninstall.py | Remove the LaunchAgent |
hotkey.py and inject.py are isolated so a future Windows port only needs to
swap those two files.
Not supported yet. The platform-specific pieces are isolated for a future port.
MIT © 2026 Dulen Weerasinghe
Local-first, system-wide push-to-talk dictation for macOS (Apple Silicon).
Hold a hotkey, speak, release — your words are transcribed on-device and pasted into whatever app is focused (Slack, VS Code, browser, anywhere). Transcription and the AI cleanup run locally on the Apple GPU via MLX, so your audio never leaves your machine and it works with wifi off. Voca learns your custom vocabulary and corrections, so it gets better over time.
Built and tested on a MacBook Air (Apple M4).
large-v3-turbo stays warm in memory; ~1–2s per dictation.Requires macOS on Apple Silicon (M1–M4). ~3.4 GB disk for the models (speech + on-device AI cleanup).
Voca.dmg from the latest release..dmg and drag Voca into your Applications folder.On first run:
A 🎙️ icon appears in the menu bar and Voca starts automatically at login.
Note: updating an unsigned app can reset its Accessibility permission. If the hotkey stops responding after an update, re-enable Voca under Accessibility.
Menu bar states: … loading · 🎙️ ready · 🔴 recording · ⏳ transcribing / cleaning up · ⚠️ error.
Click the 🎙️ icon:
Config lives at ~/Library/Application Support/Voca/config.json:
| Key | Default | Meaning |
|---|---|---|
hotkey | right option | Hold key. Also: left option, right/left command, right/left control, right/left shift |
model | mlx-community/whisper-large-v3-turbo | STT model (set to mlx-community/whisper-large-v3 for higher accuracy, slower) |
restore_clipboard | true | Restore your clipboard after pasting |
paste_method | paste | paste (Cmd+V) or type (keystrokes) |
mute_system_audio | true | Mute your Mac's audio while recording so the mic doesn't capture music/video |
boost_quiet_speech | true | Auto-amplify soft/low-volume voices before transcription |
streaming | false | false = transcribe the whole clip on release (accuracy-first). true = near-real-time, phrase-by-phrase while you hold |
cleanup_backend | local | AI cleanup: local (free, on-device), cloud (Anthropic, your own key), or off (rule-based punctuation only) |
cleanup_model_local | mlx-community/Qwen2.5-3B-Instruct-4bit | On-device cleanup model (downloaded once) |
cleanup_model_cloud | claude-haiku-4-5-20251001 | Model used only when cleanup_backend is cloud |
anthropic_api_key | "" | Only for cloud; the ANTHROPIC_API_KEY env var takes precedence |
Changes apply after you quit and reopen Voca (from the menu bar icon).
By default, cleanup runs on-device with a small local model — free, offline, and
private. If you prefer the highest quality and don't mind it being a paid, online
service, set cleanup_backend to cloud and provide your own Anthropic API key
(via the ANTHROPIC_API_KEY environment variable or the anthropic_api_key config
key). In cloud mode, your transcribed text (never your audio) is sent to Anthropic
for cleanup. Cloud is off unless you explicitly enable it. Set cleanup_backend
to off to skip AI cleanup entirely and use the lightweight rule-based punctuation.
Your vocabulary and corrections are stored in
~/Library/Application Support/Voca/voca.sqlite.
Voca.dmg and replace the app in Applications.git clone https://github.com/DulenW/voca.git && cd voca
python3.12 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python scripts/setup_models.py # download models (~3.7 GB, one time)
python scripts/install.py # run as a login-item LaunchAgent
scripts/uninstall.py removes the LaunchAgent; python main.py runs it once
without installing.
Build the app + .dmg:
pip install -r requirements-dev.txt
bash scripts/build_app.sh # -> dist/Voca.app and dist/Voca-1.4.0.dmg
⚠️ icon. Accessibility isn't granted. Enable
Voca under System Settings → Privacy & Security → Accessibility, then quit
and reopen Voca. (After an app update you may need to re-enable it.)hotkey in the config.CGEventTap on the main run loop waits for the hotkey.
No polling, no open mic — ~0% CPU.All transcription, AI cleanup, and pasting run on a single background worker
thread, so the menu bar and Quit always stay responsive. Custom vocabulary is
passed to Whisper as an initial_prompt (and to the cleanup model) to bias
spelling.
off, the rule-based punctuation model has a 512 word-piece limit (~350–400
spoken words); words beyond that still appear but won't get added
commas/periods. AI cleanup (the default) doesn't have this limit. Either way,
dictating in normal sentence-sized bursts works best.| File | Role |
|---|---|
main.py | Menu bar app (rumps); wires everything together |
audio.py | Mic capture (16 kHz mono float32) + live phrase chunking |
chunker.py | Splits a live stream into phrases (streaming mode) |
transcribe.py | mlx-whisper wrapper; quiet-boost, loads once, offline |
speech.py | Speech gate (energy + Silero VAD) — skips silence/noise |
enhance.py | AI cleanup + smart formatting (local MLX / cloud / off) |
punctuate.py | Rule-based English punctuation (used when cleanup is off) |
formatting.py | Capitalization, end marks, spacing |
corrections.py | SQLite learning layer (vocab + corrections) |
inject.py | Clipboard + paste via Quartz — platform-specific |
hotkey.py | Push-to-talk CGEventTap — platform-specific |
sysaudio.py | Mute system output while recording — platform-specific |
config.py | Load/save JSON config |
firstrun.py | First-launch model download |
uninstall.py | "Uninstall Voca…" — remove models + app (to Trash) |
setup.py | py2app build config for Voca.app |
scripts/build_app.sh | Build Voca.app + .dmg |
scripts/setup_models.py | One-time model download (source install) |
scripts/install.py | Install/reload the LaunchAgent (source install) |
scripts/uninstall.py | Remove the LaunchAgent |
hotkey.py and inject.py are isolated so a future Windows port only needs to
swap those two files.
Not supported yet. The platform-specific pieces are isolated for a future port.
MIT © 2026 Dulen Weerasinghe