Dictate is a local-first macOS dictation product. It lets you speak into any app, transcribes on-device with configurable MLX-backed models, and pastes the result back where you were writing.
The goal is a fast, private, customizable dictation workflow for people who write a lot: notes, messages, docs, prompts, email, and code-adjacent text.
- A menu-bar dictation workflow powered by Hammerspoon.
- A native macOS companion app for history and settings.
- A small Python CLI transcription engine.
- Configurable local models, with Qwen ASR as the default.
- Local transcript history with latency and model metadata.
- macOS on Apple Silicon.
- Python 3.12 via
uv. ffmpeg.- Hammerspoon for the current global tray/hotkey workflow.
Install system tools:
brew install ffmpeg
brew install --cask hammerspoonInstall uv if needed:
curl -LsSf https://astral.sh/uv/install.sh | shClone the repo:
git clone git@Github.com:ash-ishh/dictate.git
cd dictateCreate the Dictate config:
uv run --python 3.12 dictate init-configConfirm models are available:
uv run --python 3.12 dictate modelsBuild the native companion app:
cd macos/DictateApp
./package-app.sh
open .build/Dictate.appSet up Hammerspoon:
mkdir -p ~/.hammerspoon
nano ~/.hammerspoon/init.luaAdd this, replacing the path with your checkout:
hs.allowAppleScript(true)
if hs.ipc then hs.ipc.cliInstall() end
local ok, err = pcall(function()
dofile("/path/to/dictate/hammerspoon.lua")
end)
if not ok then
hs.alert.show("Dictate failed to load: " .. tostring(err))
print("Dictate failed to load: " .. tostring(err))
endOpen or reload Hammerspoon:
open -a HammerspoonIf Hammerspoon is already running:
hs -c 'hs.reload()'Grant permissions:
System Settings → Privacy & Security → Accessibility → Hammerspoon
System Settings → Privacy & Security → Microphone → Hammerspoon
The native app may also request permissions if you use native recording later:
System Settings → Privacy & Security → Accessibility → Dictate
System Settings → Privacy & Security → Microphone → Dictate
Click into any text input, then press:
Cmd + S
Speak, then press again:
Cmd + S
Dictate records, transcribes locally, and pastes into the app that was focused when recording started.
Tray menu options include:
- Start or stop recording.
- Switch model.
- Open app.
- Play last recording.
- Copy recent transcripts.
The native app shows transcript history and settings.
uv run --python 3.12 dictate transcribe /path/to/audio.wav --model mlx_audio_qwen3_asrWrite outputs to files:
uv run --python 3.12 dictate transcribe /path/to/audio.wav \
--model mlx_audio_qwen3_asr \
--output-json /tmp/dictate.json \
--output-text /tmp/dictate.txtModels are configured in:
~/.dictate/config.json
Qwen ASR is the default model. You can switch models from the tray menu or by editing the config.
Example:
{
"default_model": "mlx_audio_qwen3_asr",
"python": "3.12",
"models": {
"mlx_audio_qwen3_asr": {
"backend": "mlx_audio_stt",
"model_name": "mlx-community/Qwen3-ASR-0.6B-8bit",
"language": "English",
"extra_args": []
}
}
}Some legacy Whisper presets use an insanely-fast-whisper checkout. If needed, set it up next to this repo or set DICTATE_IFW_REPO before running dictate init-config.
git clone https://github.com/Vaibhavs10/insanely-fast-whisper.git
cd insanely-fast-whisper
git fetch origin pull/273/head:pr-273
git checkout pr-273Build a local app bundle:
cd macos/DictateApp
./package-app.shThe app is written to:
macos/DictateApp/.build/Dictate.app
For GitHub releases, zip the built app instead of committing .build:
cd macos/DictateApp
ditto -c -k --keepParent .build/Dictate.app Dictate.app.zipUpload Dictate.app.zip to a GitHub Release.
Dictate stores local configuration and history under:
~/.dictate/config.json
~/.dictate/history.jsonl
~/.dictate/native-settings.json
~/.dictate/native-history.json
~/.dictate/recordings/
Menu bar / Hammerspoon
├─ records microphone audio with ffmpeg
├─ remembers the focused app
├─ writes local history
└─ calls the Dictate CLI
↓
dictate transcribe <audio> --model <model-key>
↓
backend adapter
├─ mlx-audio STT models
├─ Qwen ASR default
└─ legacy insanely-fast-whisper MLX presets
↓
plain transcript
↓
Paste into the original app
↓
Native Dictate app reads history and settings
To add a backend, implement it in:
src/dictate/cli.py
Then add a model entry in ~/.dictate/config.json with a distinct backend value.
- Native global hotkey support, eventually replacing the Hammerspoon dependency.
- Model manager: show available models, downloaded status, download/remove actions, and warm-up controls.
- LLM pass to refine dictated input before paste.
- Personalized dictionary for names, product terms, acronyms, and writing style.
- Per-app formatting rules.
- Better onboarding and permissions flow.
- Signed and notarized macOS releases.