A local, private replacement for macOS "Speak Selection": press ⌥⎋ (Option–Escape) anywhere and the current selection is read aloud by an MLX text-to-speech model running entirely on your Mac. Press ⌥⎋ again and it stops instantly.
Download the latest release (Developer ID–signed .app, macOS 14+ · Apple Silicon) · Website & demo: mlxread-web.pages.dev
- Menu-bar utility (no Dock icon), SwiftUI + AppKit at the edges
- Local synthesis via mlx-audio-swift — Kokoro 82M (default, 54 voices) or Soprano 80M (fast, English)
- Selection capture via the Accessibility API, with a clipboard-preserving ⌘C fallback for apps that don't expose their selection
- No network after model download, no telemetry, no logging of your text
- Apple Silicon Mac
- macOS 14 or newer (built and verified on macOS 26.5 / Xcode 26.6)
- ~0.6 GB disk for both models (either alone is enough)
brew install xcodegen # once
xcodegen generate # regenerates MLXRead.xcodeproj from project.yml
script/build_and_run.sh # build (xcodebuild) + launch
script/build_and_run.sh --verify # build, launch, assert process + signatureSigning uses your local Apple Development certificate. Contributors override
the team without editing tracked files —
DEVELOPMENT_TEAM=YOURTEAM script/build_and_run.sh, or
CODE_SIGNING_ALLOWED=NO script/build_and_run.sh for a quick unsigned build
(see CONTRIBUTING.md). A stable identity is recommended so
the Accessibility grant persists across rebuilds. No paid account is required
to run locally. Plain swift build is not a supported path — mlx-swift's
Metal kernels need the Xcode build system.
- MLXRead appears as a waveform icon in the menu bar and shows a setup window.
- Grant Accessibility access (System Settings → Privacy & Security → Accessibility). This powers both selection reading and the ⌥⎋ event tap. The app monitors trust continuously: it picks up the grant without a relaunch, and if you later revoke access it removes its keyboard tap and stops any active reading immediately. Settings → Permissions shows both the permission state and whether the ⌥⎋ shortcut is actually installed.
- If Apple's built-in Speak selection shortcut is enabled and set to ⌥⎋, disable or reassign it under System Settings → Accessibility → Spoken Content. MLXRead will not change that setting for you.
- Open Settings → Models and download a model (Kokoro ~360 MB or
Soprano ~200 MB, from Hugging Face into
~/Library/Application Support/MLXRead/Models). Kokoro fetches small pronunciation assets on its first synthesis; after that everything is offline.
| Action | Result |
|---|---|
| ⌥⎋ with text selected | Selection is captured, synthesized sentence-by-sentence, playback starts as soon as the first chunk is ready |
| ⌥⎋ while reading | Generation cancelled, playback stopped, queue cleared — immediately |
| Menu bar → Read Selection / Stop | Same as the shortcut |
| Settings → Voice | Model, voice (Kokoro), speed (0.5–2×), test phrase |
Long selections are truncated at a configurable limit (default 20,000 characters) at a word boundary; truncation is indicated in the menu.
MLXRead auto-updates via Sparkle 2 with EdDSA-signed appcasts over HTTPS — every update is cryptographically verified before install. Check manually from the menu bar (Check for Updates…) or Settings → General; automatic daily checks are on by default. Update controls only appear in a build configured with a real feed and public key; source/dev builds keep the updater inactive. Maintainer setup and the release/signing process: docs/updates.md.
After a model is downloaded (plus one first synthesis for Kokoro's G2P assets), synthesis requires no network. Verification procedure: docs/privacy.md.
Selected text never leaves the machine, is never written to disk, and never
appears in logs (lengths and timings only — verifiable with
script/build_and_run.sh --logs). No analytics, no crash reporting. Full
statement and enforcement points: docs/privacy.md.
script/test.sh # unit tests (fast, no network, no models)
script/test.sh --integration # + real model download & synthesis tests
script/test.sh --ui # + UI launch smoke test
script/benchmark.sh [soprano|kokoro|all] # measured synthesis benchmark
script/package.sh # → dist/MLXRead.app + dist/MLXRead.zip (signed)
script/notarize.sh # notarize + staple + update the GitHub releasescript/notarize.sh builds (if needed), notarizes with Apple, staples the
ticket, re-zips, and updates the GitHub release — a single command:
./script/notarize.shThe first run prompts once for your Apple ID and an app-specific password (create one at appleid.apple.com → Sign-In & Security), storing them in your Keychain; every run after is automatic. After notarization, users no longer see a Gatekeeper prompt on first launch.
Measured results for this machine are recorded in docs/testing.md.
Status and evidence: docs/system-voice-provider.md.
A marketing + demo site lives in website/ — Leptos SSR with an
interactive ⌥⎋ demo, deployed to Cloudflare Pages at
https://mlxread-web.pages.dev. Details in
website/README.md.
- ⌥⎋ does nothing — check Settings → Permissions; the event tap needs Accessibility access. After granting, use "Recheck". If another app also taps ⌥⎋ (e.g. Apple Speak Selection), resolve the conflict.
- "No selected text was found" — the frontmost app reported no selection. For apps without Accessibility text (some Electron apps, protected fields), enable the clipboard fallback in Settings → General.
- "does not expose its selection" — AX failed and the fallback is disabled or also failed. Secure input fields intentionally block both.
- Model download failed — retry from Settings → Models; partial downloads are detected and re-fetched. Check disk space.
- First read after launch is slow — that's the one-time model load (see benchmarks); the model then stays warm.
- No audio — check the selected output device; the app reports "audio device unavailable" if the engine cannot start.
- Kokoro/Soprano generate per sentence-chunk, not sample-streaming: time to first audio is one chunk's synthesis (~1–2 s warm for Kokoro on M-series; see docs/testing.md for measured values).
- Kokoro cancellation takes effect at forward-pass boundaries (≤ one chunk); audible playback still stops instantly.
- Soprano ignores voice selection (single-voice model) and is English-only.
- The clipboard fallback cannot capture selections in apps that block synthetic ⌘C or use secure input; the clipboard is restored only when nothing else wrote to it mid-capture (by design).
- PDF viewers must expose a text layer through Accessibility or respond to ⌘C for capture to work.
- The published
.appis Developer ID–signed, hardened-runtime, and notarized by Apple — it opens without a Gatekeeper prompt. Re-cut a notarized release anytime with./script/notarize.sh.
MLXRead is MIT-licensed (LICENSE). Bundled dependencies and model weights: THIRD_PARTY_NOTICES.md.