Local voice-to-text dictation for macOS. Hold a hotkey, speak, release, and the text is pasted into the active app. The app runs locally on Apple Silicon using Whisper via MLX.
- Dictates into any macOS app with a global hotkey
- Uses local Whisper inference on Apple Silicon
- Shows a lightweight on-screen listening overlay
- Supports direct transcription and translation-to-English flows
- Can run from source or as a packaged
.app
Behavio Dictate was inspired by the original Whisper Dictate project.
For a ready-to-run macOS build, use the latest GitHub Release:
https://github.com/behavio1/behavio-dictate/releases/latest
Release assets are built for macOS on Apple Silicon.
This repository is currently configured with Behavio-specific defaults:
- app name:
Behavio Dictate - default language: Polish (
pl) - default hotkey: right Command (
cmd_r) - default model:
mlx-community/whisper-large-v3-turbo - auto-paste: enabled
You can change these values in config.yaml.
We recommend large-v3-turbo as the default model for this app. It gives the best overall balance of speed and quality and is the recommended model for Behavio Dictate.
The hotkey is only relevant in global mode, meaning when you run the app with --global or launch the packaged macOS app.
You can configure it in three places:
Edit the hotkey field in config.yaml:
hotkey: "cmd_r"Use this when you want your preferred shortcut to be the default every time you run the app.
Pass --hotkey together with --global:
python dictate.py --global --hotkey esc
python dictate.py --global --hotkey f6
python dictate.py --global --hotkey rThis overrides the value from config.yaml only for the current launch.
If you use run-whisper-dictate-pl.command, the hotkey is also set there explicitly:
./run-whisper-dictate-pl.commandThat script currently runs:
python dictate.py --config config.yaml --global --language pl --model mlx-community/whisper-large-v3-turbo --hotkey cmd_rIf you want that launcher to use another shortcut by default, update the --hotkey ... argument in run-whisper-dictate-pl.command.
These values are supported by the app:
fnorglobe: the fn/Globe key on a Mac keyboardescorescape: Escapectrl,ctrl_l,ctrl_r: Control keyalt,alt_l,alt_r,option,option_l,option_r: Option/Alt keycmd,cmd_l,cmd_r: Command keyshift,shift_l,shift_r: Shift keyspace,tab,enter: common keyboard keysf1tof12: function keys- any single character such as
r,t, or;
Examples:
hotkey: "cmd_r": right Commandhotkey: "ctrl_r": right Controlhotkey: "f8": F8hotkey: "r": letter R
--global: enables system-wide hotkey mode--hotkey KEY: chooses which key starts and stops recording--config FILE: loads a different config file instead of the defaultconfig.yaml
Example with all three together:
python dictate.py --config config.yaml --global --hotkey ctrl_rIf you use fn, macOS should not reserve it for the emoji picker:
System Settings > Keyboard > Press fn key to > Do Nothing
- macOS on Apple Silicon (
arm64) - Python 3.11+
- Homebrew
- PortAudio:
brew install portaudio
The default model is not committed to the repository and is not embedded in the source tree.
On first launch, the app downloads the configured model from Hugging Face and caches it locally. With the current default model, expect roughly 1.5 GB to be downloaded once.
After the model is cached, transcription works locally without needing an active internet connection.
git clone https://github.com/behavio1/behavio-dictate.git
cd behavio-dictate
chmod +x setup.sh download-model.sh run-whisper-dictate-pl.command scripts/build_macos_dist.sh
./setup.shRun in terminal mode:
source venv/bin/activate
python dictate.pyRun in global mode:
source venv/bin/activate
python dictate.py --globalRun with the repository defaults:
./run-whisper-dictate-pl.commandchmod +x setup.sh download-model.sh run-whisper-dictate-pl.command scripts/build_macos_dist.sh
./setup.shMain app behavior is configured in config.yaml.
Current default configuration:
backend: "mlx"
model: "mlx-community/whisper-large-v3-turbo"
language: "pl"
hotkey: "cmd_r"
sound_on_start: true
sound_on_stop: true
auto_paste: true
show_overlay: true
overlay_position: "top_center"
trailing_space: true
device: nullbackend: usemlxon Apple Silicon Macsmodel: Whisper model repo id or local pathlanguage: default spoken language, for examplepl,en, orautohotkey: key used in global mode to start and stop recordingauto_paste: iftrue, the app pastes the result automatically; iffalse, it only copies to clipboardshow_overlay: shows the floating listening indicator while recordingdevice: microphone device index, ornullto use the system default input
Recommended setting for this app:
model: "mlx-community/whisper-large-v3-turbo"This is the recommended model for Behavio Dictate because it gives the best overall speed/quality tradeoff.
If you want a local offline model path instead of a remote Hugging Face id:
./download-model.shThen update config.yaml, for example:
model: "models/whisper-large-v3-turbo-mlx"Use one of these approaches:
- persistent default in
config.yaml - one-off override with
python dictate.py --global --hotkey ... - launcher-specific override in
run-whisper-dictate-pl.command
Examples:
hotkey: "cmd_r"
hotkey: "ctrl_r"
hotkey: "f8"
hotkey: "r"For global mode or the packaged app, enable:
MicrophoneAccessibilityInput Monitoring
If you use fn, also set:
System Settings > Keyboard > Press fn key to > Do Nothing
Useful commands:
source venv/bin/activate
python dictate.py --list-devices
python dictate.py --global --debug-keys
python dictate.py --testFrom source in terminal mode:
source venv/bin/activate
python dictate.pyFrom source in global mode:
source venv/bin/activate
python dictate.py --globalWith the repository launcher:
./run-whisper-dictate-pl.commandBuild a distributable .app and release ZIP locally:
./scripts/build_macos_dist.shThis produces release-ready files in dist/.
git clone git@github.com-behavio1:behavio1/behavio-dictate.git
cd behavio-dictate
chmod +x setup.sh download-model.sh run-whisper-dictate-pl.command scripts/build_macos_dist.sh
./setup.shMake sure config.yaml contains the values you want to ship in the app bundle, especially:
modellanguagehotkeyauto_pasteshow_overlay
The build includes config.yaml inside the packaged app, so these defaults become the initial shipped defaults.
./scripts/build_macos_dist.shThis script:
- checks that you are on macOS Apple Silicon
- uses
venv/bin/python - installs PyInstaller if needed
- builds
Behavio Dictate.app - creates release ZIP archives in
dist/
Expected files:
dist/Behavio Dictate.appdist/Behavio Dictate-macOS.zipdist/Behavio Dictate-share.zip
Before publishing, test at least this:
- open
dist/Behavio Dictate.app - confirm microphone permission prompt works
- confirm overlay appears during recording
- confirm the selected hotkey starts and stops recording
- confirm text is copied or pasted as expected
git status
git add .
git commit -m "Describe your release change"
git push origin maingit tag -a v0.1.1 -m "Behavio Dictate 0.1.1"
git push origin v0.1.1Example with GitHub CLI:
gh release create v0.1.1 \
"dist/Behavio Dictate-share.zip#Behavio Dictate for macOS" \
"dist/Behavio Dictate-macOS.zip#Behavio Dictate app archive" \
--repo behavio1/behavio-dictate \
--title "Behavio Dictate 0.1.1" \
--notes "Short release notes here"For end users, the preferred download is usually:
Behavio Dictate-share.zip
It contains the app plus the short macOS usage note.
Download a model explicitly for offline-first setup:
./download-model.shNote: the repository default uses the remote model id mlx-community/whisper-large-v3-turbo. If you download a local copy with download-model.sh, the matching local path will be models/whisper-large-v3-turbo-mlx.
Common MLX models:
| Model | Size | Remote repo id |
|---|---|---|
| tiny | ~75 MB | mlx-community/whisper-tiny-mlx |
| base | ~150 MB | mlx-community/whisper-base-mlx |
| small | ~500 MB | mlx-community/whisper-small-mlx |
| medium | ~1.5 GB | mlx-community/whisper-medium-mlx |
| large-v3 | ~3.0 GB | mlx-community/whisper-large-v3-mlx |
| large-v3-turbo | ~1.6 GB | mlx-community/whisper-large-v3-turbo |
Recommended for this app: large-v3-turbo.
Main settings live in config.yaml:
backend: "mlx"
model: "mlx-community/whisper-large-v3-turbo"
language: "pl"
hotkey: "cmd_r"
auto_paste: true
show_overlay: true
sound_on_start: true
sound_on_stop: true
trailing_space: trueAvailable hotkeys include fn, esc, ctrl_r, cmd_r, space, tab, enter, f1-f12, and single-character keys.
Global mode requires:
MicrophoneAccessibilityInput Monitoring
If macOS blocks the packaged app, open it the first time via right click and Open.
.
├── README.md
├── LICENSE
├── .github/
├── assets/
├── scripts/
├── sounds/
├── dictate.py
├── overlay.py
├── config.yaml
├── setup.sh
├── download-model.sh
├── run-whisper-dictate-pl.command
└── Whisper Dictate PL.spec
Build outputs, local models, virtual environments, and backup copies are intentionally excluded from git.
dictate.pyis the main entry pointoverlay.pyhandles the floating listening indicatorWhisper Dictate PL.specbuilds the macOS app via PyInstallerscripts/build_macos_dist.shcreates the distributable app bundle and ZIP
This project builds on the ideas and codebase of the original Whisper Dictate project and has been adapted for the Behavio workflow and macOS app distribution.
MIT