Local, privacy-first voice dictation for macOS.
No cloud. No telemetry. No nonsense.
Dexter approves.
Barely.
DexDictate is a local-first macOS menu-bar dictation app for Apple Silicon Macs. It records from your microphone, transcribes on-device with Whisper, applies local post-processing such as voice commands and vocabulary correction, and then saves or inserts the result according to your output settings.
This is not a cloud wrapper.
It does not "phone home."
Use the newest release artifact first. Current latest release: v1.8.0
Release page: v1.8.0
Download one of these Apple Silicon artifacts:
DexDictate-1.8.0-macos-arm64.dmgDexDictate-1.8.0-macos-arm64.zipDexDictate-1.8.0-macos-arm64-SHA256SUMS.txt
Standard .dmg flow:
- Open the disk image.
- Drag
DexDictate.appinto/Applications. - Launch the app and complete onboarding.
git clone https://github.com/westkitty/DexDictate_MacOS.git
cd DexDictate_MacOS
./build.sh./build.sh will:
- fetch and verify the bundled Whisper model if it is missing
- build the app and the
VerificationRunnerhelper - sign the bundle
- install into
/Applicationswhen writable, otherwise~/Applications
Useful variants:
./build.sh --user
./build.sh --system
INSTALL_DIR=/Applications ./build.sh- macOS 14 or later
- Apple Silicon (
arm64) - Xcode 15 or Command Line Tools (for source builds)
Intel Macs are not supported. The build script rejects x86_64 environments.
On first run, macOS will ask for:
- Microphone
- Accessibility
- Input Monitoring
Grant them.
If you skip them, it won't work.
This is macOS, not a suggestion.
- Menu-bar utility with four-step onboarding flow
- Local Whisper transcription via SwiftWhisper
- Bundled default model:
tiny.en - Global trigger capture from keyboard shortcuts or mouse buttons
- Trigger modes:
Hold to TalkandClick to Toggle - Input device selection with system-default fallback
- Live microphone level feedback
- Silence timeout and utterance-end tuning controls
- Optional floating HUD while recording/transcribing
- Optional sound cues
- System-wide dictation into whatever app currently holds focus
- Auto-paste toggle in the menu-bar dropdown — click the
Auto-pastepill to switch it on or off; the change persists and applies to the next dictation - Auto-paste off inserts nothing and leaves your clipboard untouched — the result is still transcribed and kept in history
- Selected text is replaced — select
redinThe red fox, dictateblue, getThe blue fox - Direct Accessibility insertion where the target supports it, with clipboard paste as the fallback
- Clipboard-only fallback for likely secure fields
- Per-app insertion overrides by bundle identifier
- Replace-field mode (Cmd+A then paste) for single-input targets such as address bars and search fields — not for documents or multi-line fields
- Focused-element identity matching before paste delivery to prevent wrong-target insertion
- Editable-element validation before paste (role-aware; fails open for ambiguous AX contexts)
- Original clipboard contents are restored after an auto-paste
- Launch-at-login support through
SMAppService
- Sits beside the Auto-paste pill in the menu-bar dropdown, and is also bound to
⌃⌥⌘Z - Enabled only once DexDictate has confirmed what actually landed — either a verified Accessibility write, or a clipboard paste whose result it re-read on the saved field. This covers TextEdit and supported browser composers
- Removes only the text that dictation inserted, restoring what was there before — including text that was replaced from a selection
- Refuses safely if you edited the text afterwards, rather than overwriting your edit; the control stays visible and states the reason
- Always visible, with the reason shown in place when it is unavailable — no hover required
- Placeholder protection: an empty web composer reports its prompt text through the Accessibility API as if it were real content. DexDictate never treats that as editable text, never inserts it, and never writes it back on undo — the field receives only what you dictated
- Undoing a dictation in an empty composer restores it to empty, letting the app render its own placeholder again
- DexDictate asks the target application to enable its accessibility tree, which is what makes verified insertion and undo possible in Chromium-based browsers
- Built-in voice commands for editing and formatting
- Bundled vocabulary packs
- User vocabulary layering and correction rules
- Custom
Dex <keyword>commands - Optional profanity filtering
- Optional focused-field context injection to prime transcription accuracy for proper nouns and continuing sentences
- Local-only runtime transcription (no cloud path)
- Safe handling for likely password/secure fields
Safe Modepreset for stricter behavior- No telemetry and no speech analytics pipeline
- Detachable transcription history window
- Quick settings popover that expands wider (not taller) with a spring animation
- Randomized launch animation drawn from a pool of original short films
- Help content backed by repository-owned assets
VerificationRunnerexecutable for verification checks- Benchmark scripts and sample corpus
- Release packaging for
.zipand.dmgwith the localDexDictate Developmentsigning identity - Release validation for bundle integrity, architecture, signing, entitlements, and hashes
- GitHub Actions CI for
swift buildandswift testonmainpushes and pull requests, with required status checks gating merges intomain
- Launch the app
- Trigger dictation via shortcut (default configured in-app)
- Speak
- Text appears where your cursor is — replacing your selection if you had one
That's the entire point.
To keep a result without touching the focused app, turn Auto-paste off in the
menu-bar dropdown: the transcription is still saved to history, and your clipboard
is left exactly as you had it. To take an insertion back, use Undo Last Dictation
next to the same pill, or press ⌃⌥⌘Z.
- Undo is not offered for every delivery. It is armed only when DexDictate can prove exactly what changed, and it says why when it cannot.
- A paste landing at the very start or very end of existing text produces the same result as a placeholder that survived alongside it. Those two cannot be told apart, so such deliveries are left unverified and undo stays disabled rather than risk restoring the wrong text.
- Verified browser undo depends on the browser exposing its accessibility tree. DexDictate requests this, but an app that declines will fall back to an unverified paste with undo unavailable.
- Replace-field mode is for single-input targets only; it will clear a document or multi-line field.
- Apple Silicon only. Intel Macs are not supported.
- All audio is processed locally
- No network calls for transcription
- No analytics, tracking, or logging of user speech
- No hidden services
If something leaves your machine it's because you explicitly added it.
./scripts/fetch_model.sh
swift build
swift test
swift run VerificationRunner
./build.shUseful commands:
make check(lint + build + test) — local quality gate; runmake helpto list all targets./build.sh [--user|--system] [--release](--releaserequires theDexDictate Developmentsigning identity; local non-release builds may use ad-hoc signing)./scripts/setup_dev_env.sh./scripts/fetch_model.sh./scripts/run_quality_paths.sh./scripts/verify_audio_route_recovery.sh./scripts/benchmark.sh --audio <wav>python3 scripts/benchmark.py --corpus-dir <dir>./scripts/benchmark_speech_matrix.sh --corpus-dir sample_corpus./scripts/benchmark_regression.sh <wav> [baseline_ms]./scripts/trim_benchmark_corpus.sh <input_dir> [output_dir]./scripts/validate_release.sh [path_to_app_bundle]
.
├── Sources/
│ ├── DexDictate/ # menu-bar app target
│ ├── DexDictateKit/ # transcription, settings, permissions, output, resources
│ └── VerificationRunner/ # verification and benchmark helper executable
├── Tests/DexDictateTests/ # unit and integration tests
├── scripts/ # build, benchmark, setup, and validation tooling
├── docs/ # feature inventory, help content, and long-form project docs
├── assets/ # artwork, marketing images, icons, and source visuals
├── sample_corpus/ # benchmark sample audio and transcripts
├── templates/ # Info.plist template used during bundle assembly
├── build.sh # canonical build, install, and release entry point
└── Package.swift # Swift Package Manager manifest
- Feature inventory
- Security audit
- Verification report
- Contributing guidance
- Speech engine exploration
- Moral architecture
Issues and pull requests are welcome, but review and merge are not guaranteed. Keep changes specific, test-backed and realistic about current project scope.
DexDictate is not trying to be everything.
It is:
- Local
- Fast
- Private
- Minimal
If you want cloud AI orchestration this is the wrong tool.
This repository is released under the Unlicense as public domain. You could use it in a mutual aid project. You could use it as a custom accessibility tool like I do. Or you could even sell it if you felt like embarrassing yourself in public. You can do whatever you want with it and you are encouraged to do so.
Remain ungovernable so Dexter approves.
See LICENSE.
Dexter does not celebrate features.
Dexter tolerates correctness.
This passes.
Dexter is a small, tricolor Phalène dog with floppy ears and a perpetually unimpressed expression... ungovernable, sharp-nosed and convinced he is the quality bar. Alert, picky, dependable, and devoted to doing things exactly his way: if he is staring at you, assume you made a mistake. If he approves, it works.
Thanks to the Whisper team for developing such a useful tool.


