{"slug": "on-device-and-open-source-whisper-dictation", "title": "On-device and open source Whisper dictation", "summary": "A macOS menu bar app called Local Flow runs OpenAI's Whisper speech-to-text model locally via whisper.cpp with Metal, keeping all dictation on the user's Mac by default. Local Flow offers three built-in models (Best, Classic, Tiny) downloaded in-app from Hugging Face, accepts any ggml Whisper model by Hugging Face link or local file, and can optionally route clips to OpenAI, ElevenLabs Scribe or Gemini using the user's own API key with automatic fallback to the local model. The app requires macOS 14 or later, is distributed as Local-Flow.dmg through GitHub Releases, and deletes each voice recording 5 minutes after transcription while wiping transcripts when the app quits or after 12 hours.", "body_md": "You speak faster than you type. Local Flow keeps all voice dictation private. Nobody will train on your data, and all transcription runs locally.\n\n- **Hold to talk.** Fn by default; pick any key in Settings. Double-tap it to keep listening hands-free, tap again to stop. A small Liquid Glass bar shows a live waveform while you speak (it adapts to your mic level, so a quiet input still moves); the text goes straight to your cursor. Pick which microphone to use in Settings.\n- **Local by default.** Whisper (whisper.cpp with Metal) runs on your Mac. Three models to pick from — Best (recommended), Classic, Tiny — downloaded in-app from Hugging Face; or add any ggml Whisper model by Hugging Face link or local file (Settings → Transcription → Custom model).\n- **Or bring your own key.** Cloud mode sends the clip to OpenAI, ElevenLabs Scribe or Gemini with your key, with retries and automatic fallback to the local model. Optional*Mixture of experts* : every service you have a key for transcribes, then an OpenAI model merges the results — slower, but more accurate. Setup looks for keys in your shell files (`~/.zshrc` ,`~/.zprofile` , … and the files they`source` ) and shows what it found; nothing is saved until you click*Import from shell* .\n- **Opens at login** once setup is done (Settings → General to turn it off).\n- **Files too.** Drop an audio or video file on any Local Flow window (or click*Choose File…* ), right-click it in Finder → Services →*Transcribe with Local Flow* , or use*Transcribe File…* in the menu bar: the transcript lands in History and on your clipboard. Long recordings are cut at pauses into parts of up to 5 minutes (cloud services cap uploads at 20–25 MB), transcribed one after another with progress on the bar, and joined;*Cancel File Transcription* in the menu bar stops it.\n- **One dictionary.** A plain list of names, products and jargon. Sent to the model as a hint and used to fix spelling on-device. Setup lets you add word packs for your field (engineering, product, sales, legal…).\n- **Cleanup that never leaves your Mac.** Ums and uhs, capitalization, punctuation and \"new paragraph\" are handled with rules, not a model. It also knows where you are typing: terminals, code editors and browser address bars get lowercase with no sentence punctuation (`git status` ,`example.com` ); chat apps skip the final period. On by default, one toggle to turn off.\n- **Short-lived history.** Transcripts stay on your Mac only while the app is running — wiped when you quit (and on launch), and never kept longer than 12 hours. The voice recording behind each one is deleted after 5 minutes (long enough for a Retry) and can't be played back in the app.\n- **Self-updating.** Checks GitHub Releases once a day (also while it stays open) and installs the new DMG in place.\n\nDownload the latest `Local-Flow.dmg` from [Releases](https://github.com/jzone3/local-flow/releases/latest), drag Local Flow to Applications and open it. Grant Microphone and Accessibility access when asked (Accessibility is what lets it see the hotkey in other apps and paste the text).\n\nRequires macOS 14 or later. Liquid Glass needs macOS 26; older systems get a frosted bar.\n\n```\nbrew install xcodegen\nscripts/fetch-whisper.sh          # downloads the pinned whisper.cpp xcframework into Vendor/ (checksum verified)\nxcodegen generate\nxcodebuild -project LocalFlow.xcodeproj -scheme LocalFlow -configuration Debug -derivedDataPath build CODE_SIGNING_ALLOWED=NO build\nopen \"build/Build/Products/Debug/Local Flow.app\"\n```\n\nTests for the pure-Swift core (cleanup rules, dictionary spelling fixes, env-key scan, version compare) run with `scripts/test-core.sh`.\n\n```\nLocalFlow/\n  App.swift                 menu bar app, windows\n  Core/                     TextCleanup, CleanupProfile, Vocabulary(+Packs), EnvironmentKeys, Hotkey, MixtureOfExperts, AudioChunker, Version — no AppKit, covered by Tests/\n  Capture/                  HotkeyMonitor, AudioRecorder (16 kHz mono), AudioInputDevice, TextInserter (paste), DictationController\n  Transcription/            TranscriptionProvider protocol, LocalWhisper (+ model downloader), CloudProviders, TranscriptionService\n  Model/                    AppSettings, Keychain, HistoryStore (wiped on quit, 12 h purge), UpdateChecker, Welcome\n  UI/                       DictationBar (Liquid Glass), Settings, Onboarding, History, PackPicker, Glass (styles), Icon\n```\n\n1. Add a case to `CloudProviderID` in`LocalFlow/Transcription/CloudProviders.swift` with its models, key URL and env var names.\n2. Write a `TranscriptionProvider` that turns a`TranscriptionRequest` (16 kHz samples + WAV + dictionary) into text. Throw`TranscriptionError.http` /`.network` so retries and fallback work.\n3. Return it from `makeProvider(model:)` . Settings and onboarding pick it up automatically.\n\nLocal engines work the same way: `LocalWhisper` is just another `TranscriptionProvider`.\n\n- Local mode: audio and text never leave the Mac.\n- Cloud mode: only the audio clip and the dictionary hint go to the provider you chose, using your key (stored in the Keychain). Mixture of experts sends the same clip to every service you have a key for, and their transcripts (text only) to OpenAI for merging. Local Flow has no server and keeps no logs; the providers may keep what you send under their own terms.\n- Cleanup and spelling fixes are deterministic and on-device. There is no LLM cleanup pass.\n- History (`~/Library/Application Support/Local Flow/History` ) is one`history.json` plus one`.wav` per dictation, nothing else. The whole folder is emptied when the app quits and again at launch (so a crash leaves nothing behind); Delete removes files right away; while running, the purge runs every minute (recordings go after 5 minutes, text after 12 hours), on wake and whenever the app comes to the front, and also removes orphaned audio. The folder is excluded from Time Machine so backups never keep a copy.\n- Transcripts are pasted through the pasteboard, which is restored 0.6 s later; a third-party clipboard manager could still record them, as with any dictation app.\n\nDrag Local Flow from Applications to the Trash. That leaves behind:\n\n- `~/Library/Application Support/Local Flow/` — the downloaded Whisper models (0.5–1.6 GB each) and the session history.\n- Settings: `defaults delete com.jzone3.LocalFlow`\n- Any API keys you saved, in the login keychain under service `com.jzone3.LocalFlow.keys` (accounts`openai.apiKey` ,`elevenlabs.apiKey` ,`gemini.apiKey` ; builds before 0.0.7 used`com.jzone3.LocalFlow` ): remove them in Keychain Access, or`security delete-generic-password -s com.jzone3.LocalFlow.keys -a openai.apiKey` (same for the other two).\n\nSettings → About → **Remove All Data…** does all three and quits, so only the drag to the Trash is left.\n\nSame pipeline as [New Media Writer](https://github.com/jzone3/new-media-writer): `release.yml` builds, signs, notarizes and publishes `Local-Flow-<version>.dmg` plus a stable `Local-Flow.dmg` on every push to `master` (or a `v*` tag). Push a `release-dry-run/**` branch to get the DMG as a workflow artifact without publishing.\n\nSecrets the workflow needs:\n\n| Secret | Purpose | \n|---|---|\n| `MAC_CERT_P12` | base64 Developer ID Application certificate | \n| `MAC_CERT_PASSWORD` | password for the certificate | \n| `APPLE_TEAM_ID` | Apple Developer team ID | \n| `NOTARY_KEY_P8` | base64 App Store Connect `.p8` key | \n| `NOTARY_KEY_ID` | App Store Connect key ID | \n| `NOTARY_ISSUER_ID` | App Store Connect issuer ID | \n\nMIT", "url": "https://wpnews.pro/news/on-device-and-open-source-whisper-dictation", "canonical_source": "https://github.com/jzone3/local-flow", "published_at": "2026-11-03 01:29:09+00:00", "updated_at": "2026-11-03 01:51:08.843481+00:00", "lang": "en", "topics": ["ai-tools", "artificial-intelligence", "natural-language-processing"], "entities": ["Local Flow", "Whisper", "whisper.cpp", "OpenAI", "ElevenLabs Scribe", "Gemini", "Hugging Face", "GitHub Releases"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/on-device-and-open-source-whisper-dictation", "markdown": "https://wpnews.pro/news/on-device-and-open-source-whisper-dictation.md", "text": "https://wpnews.pro/news/on-device-and-open-source-whisper-dictation.txt", "jsonld": "https://wpnews.pro/news/on-device-and-open-source-whisper-dictation.jsonld"}}