An open source desktop dictation application that converts speech to text using OpenAI Whisper. Features both local and cloud processing options for maximum flexibility and privacy.
For maintainers, contributors, and coding agents:
- Documentation map
- Agent instructions
- Technical architecture reference
- Debug logging and diagnostics
- Troubleshooting
- Windows installer build runbook
This project is licensed under the MIT License - see the LICENSE file for details. This means you can freely use, modify, and distribute this software for personal or commercial purposes.
- ☁️ EchoDraft Cloud: Sign in and transcribe instantly — no API keys needed, with free and Pro plans
- 🔐 Account System: Google OAuth and email/password sign-in with email verification
- 💳 Subscription Management: Free tier (2,000 words/week), Pro tier (unlimited), 7-day free trial
- 🎤 Global Hotkey: Customizable hotkey to start/stop dictation from anywhere (default: backtick `)
- 🤖 Multi-Provider AI Cleanup: Choose between OpenAI, Anthropic Claude, Google Gemini, or local models
- 🛡️ Protected Cleanup Policy: Fixed cleanup-only prompts preserve dictated intent and never execute dictated requests
- 🧠 Multi-Provider AI:
- OpenAI cleanup: GPT-5.6 Terra (balanced default), GPT-5.6 Luna (fast), and GPT-5.6 Sol (highest quality)
- Anthropic: Claude Opus 4.5, Claude Sonnet 4.5
- Google: Gemini 2.5 Pro/Flash/Flash-Lite
- Groq: Ultra-fast inference with Llama and Mixtral models
- Local: Qwen, LLaMA, Mistral models via llama.cpp
- 🔒 Privacy-First: Local processing keeps your voice data completely private
- 🎨 Modern UI: Built with React 19, TypeScript, and Tailwind CSS v4
- 🚀 Fast: Optimized with Vite and modern tooling
- 📱 Control Panel: Manage settings, view history, and configure API keys
- 🗄️ Transcription History: SQLite database stores all your transcriptions locally
- 🔧 Model Management: Download and manage local Whisper models (tiny, base, small, medium, large, turbo)
- ⚡ NVIDIA Parakeet: Fast local transcription via sherpa-onnx (multilingual, 25 languages)
- 🧹 Model Cleanup: One-click removal of cached Whisper models with uninstall hooks to keep disks tidy
- 🌐 Cross-Platform: Works on macOS, Windows, and Linux
- ⚡ Automatic Pasting: Transcribed text automatically pastes at your cursor location
- 🧵 Stacked Dictation: Start another recording while earlier audio is processing; jobs are delivered in recording order
- 🧭 Tray-First Interface: Use the system tray icon for dictation status, last-copy actions, settings, and app access
- 🔄 OpenAI Responses API: Using the latest Responses API for improved performance
- 🛡️ Preservation-First Cleanup: Suspicious compression, changed polarity, missing literals, or assistant-style execution triggers a model-guided repair using the rejected draft and safety reasons, then safely falls back to the original text if needed
- 🌐 Globe Key Toggle (macOS): Optional Fn/Globe key listener for a hardware-level dictation trigger
- ⌨️ Compound Hotkeys: Support for multi-key combinations like
Cmd+Shift+K - 🎙️ Push-to-Talk (Windows): Native low-level keyboard hook for true push-to-talk with compound hotkey support
- 📖 Custom Dictionary: Add words, names, and technical terms to improve transcription accuracy
- 🐧 GNOME Wayland Support: Native global shortcuts via D-Bus for GNOME Wayland users
- Node.js 18+ and npm (Download from nodejs.org)
- macOS 10.15+, Windows 10+, or Linux
- On macOS, Globe key support requires the Xcode Command Line Tools (
xcode-select --install) so the bundled Swift helper can run
-
Clone the repository:
git clone https://github.com/n-pinkerton/echo-draft.git cd echo-draft -
Install dependencies:
npm install
-
Optional: Set up API keys (only needed for cloud processing):
Method A - Environment file:
cp .env.example .env # Edit .env and add your API keys: # OPENAI_API_KEY=your_openai_key # ANTHROPIC_API_KEY=your_anthropic_key # GEMINI_API_KEY=your_gemini_key # GROQ_API_KEY=your_groq_key # MISTRAL_API_KEY=your_mistral_key
Method B - In-app configuration:
- Run the app and configure API keys through the Control Panel
- Keys are automatically saved and persist across app restarts
-
Build the application:
npm run build
-
Run the application:
npm run dev # Development mode with hot reload # OR npm start # Production mode
-
Optional: Local Whisper from source (only needed if you want local processing):
npm run download:whisper-cpp
This downloads the whisper.cpp binary for your current platform into
resources/bin/.
If you want to build a standalone app for personal use:
# Build without code signing (no certificates required)
npm run pack
# The unsigned app will be in: dist/mac-arm64/EchoDraft.app (macOS)
# or dist/win-unpacked/EchoDraft.exe (Windows)
# or dist/linux-unpacked/echodraft (Linux)Note: On macOS, you may see a security warning when first opening the unsigned app. Right-click and select "Open" to bypass this.
EchoDraft now supports multiple Linux package formats for maximum compatibility:
Available Formats:
.deb- Debian, Ubuntu, Linux Mint, Pop!_OS.rpm- Fedora, Red Hat, CentOS, openSUSE.tar.gz- Universal archive (works on any distro).flatpak- Sandboxed cross-distro packageAppImage- Portable single-file executable
Building Linux Packages:
# Build default Linux package formats (AppImage, deb, rpm, tar.gz)
npm run build:linux
# Find packages in dist/:
# - EchoDraft-x.x.x-linux-x64.AppImage
# - EchoDraft-x.x.x-linux-x64.deb
# - EchoDraft-x.x.x-linux-x64.rpm
# - EchoDraft-x.x.x-linux-x64.tar.gzOptional: Building Flatpak (requires additional setup):
# Install Flatpak build tools
sudo apt install flatpak flatpak-builder # Debian/Ubuntu
# OR
sudo dnf install flatpak flatpak-builder # Fedora/RHEL
# Add Flathub repository and install runtime
flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
flatpak install --user -y flathub org.freedesktop.Platform//24.08 org.freedesktop.Sdk//24.08
# Add "flatpak" to linux.target in electron-builder.cjs, then build
npm run build:linuxInstallation Examples:
# Debian/Ubuntu
sudo apt install ./dist/EchoDraft-*-linux-x64.deb
# Fedora/RHEL
sudo dnf install ./dist/EchoDraft-*-linux-x64.rpm
# Universal tar.gz (no root required)
tar -xzf dist/EchoDraft-*-linux-x64.tar.gz
cd EchoDraft-*/
./echodraft
# Flatpak
flatpak install --user ./dist/EchoDraft-*-linux-x64.flatpak
# AppImage (existing method)
chmod +x dist/EchoDraft-*.AppImage
./dist/EchoDraft-*.AppImageOptional Dependencies for Automatic Paste:
The clipboard paste feature requires platform-specific tools:
X11 (Traditional Linux Desktop):
# Debian/Ubuntu
sudo apt install xdotool
# Fedora/RHEL
sudo dnf install xdotool
# Arch
sudo pacman -S xdotoolWayland (Modern Linux Desktop):
Recommended: Install wl-clipboard for reliable clipboard sharing between Wayland apps:
sudo apt install wl-clipboard # Debian/Ubuntu
sudo dnf install wl-clipboard # Fedora/RHEL
sudo pacman -S wl-clipboard # ArchChoose one of the following paste tools:
Option 1: wtype (requires virtual keyboard protocol support)
# Debian/Ubuntu
sudo apt install wtype
# Fedora/RHEL
sudo dnf install wtype
# Arch
sudo pacman -S wtypeOption 2: ydotool (works on more compositors, requires daemon)
# Debian/Ubuntu
sudo apt install ydotool
sudo systemctl enable --now ydotoold
# Fedora/RHEL
sudo dnf install ydotool
sudo systemctl enable --now ydotoold
# Arch
sudo pacman -S ydotool
sudo systemctl enable --now ydotooldTerminal Detection (Optional - for KDE Wayland users):
# On KDE Wayland, kdotool enables automatic terminal detection
# to paste with Ctrl+Shift+V instead of Ctrl+V
sudo apt install kdotool # Debian/Ubuntu
sudo dnf install kdotool # Fedora/RHEL
sudo pacman -S kdotool # Archℹ️ Note: EchoDraft automatically tries paste tools in this order:
wtype→ydotool→xdotool(for XWayland apps). If no paste tool is installed, text will still be copied to the clipboard - you'll just need to paste manually with Ctrl+V.
⚠️ ydotool Requirements: Theydotoolddaemon must be running for ydotool to work. Start it manually withsudo ydotoold &or enable the systemd service as shown above.
GNOME Wayland Global Hotkeys:
On GNOME Wayland, Electron's standard global shortcuts don't work due to Wayland's security model. EchoDraft automatically uses native GNOME keyboard shortcuts via D-Bus and gsettings:
- Hotkeys are registered as GNOME custom shortcuts (visible in Settings → Keyboard → Shortcuts)
- Default hotkey is
Alt+R(backtick not supported on GNOME Wayland) - Push-to-talk mode is not available on GNOME Wayland (only tap-to-talk)
- Falls back to X11/XWayland shortcuts if GNOME integration fails
- No additional dependencies required - uses
dbus-nextnpm package
ℹ️ GNOME Wayland Limitation: GNOME system shortcuts only fire a single toggle event (no key-up detection), so push-to-talk mode cannot work. The app automatically uses tap-to-talk mode on GNOME Wayland.
🔒 Flatpak Security: The Flatpak package includes sandboxing with explicit permissions for microphone, clipboard, and file access. See electron-builder.cjs for the complete permission list.
For maintainers who need to distribute signed builds:
# Configured certificates are required for signed distribution artifacts
npm run build:mac # macOS (requires Apple Developer account)
npm run build:win # Windows (unsigned unless a code signing cert is configured)
npm run build:linux # Linux-
Choose Processing Method:
- EchoDraft Cloud: Sign in for instant cloud transcription with free and Pro plans
- Bring Your Own Key: Use your own OpenAI/Groq/AssemblyAI API keys
- Local Processing: Download Whisper or Parakeet models for completely private transcription
-
Grant Permissions:
- Microphone Access: Required for voice recording
- Accessibility Permissions: Required for automatic text pasting (macOS)
-
Name Your Agent: Give your AI assistant a personal name (e.g., "Assistant", "Jarvis", "Alex")
- Makes interactions feel more natural and conversational
- Helps distinguish between giving commands and regular dictation
- Can be changed anytime in settings
-
Configure Global Hotkey: Default is backtick (`) but can be customized
- Start the app - EchoDraft loads a persistent system tray icon
- Press your hotkey (default: backtick
) - A rising cue confirms recording and a click-throughREC` timer remains visible while the microphone is live. After one minute it can highlight Still recording as a silent reminder. - Press your hotkey again - A lower falling cue confirms recording has stopped; the floating indicator then shows transcription and cleanup progress, keeps the completed status visible for five seconds, and fades away
- Text appears - Transcribed text is automatically pasted at your cursor location, followed by a distinct completion chime
- Use tray actions - Click the tray icon to start clipboard dictation, copy the last dictation, open the Control Panel, or quit
You can press the hotkey again and record another dictation while earlier audio is processing. EchoDraft queues complete recordings in order. In insertion mode each result is inserted sequentially into its captured application target; in clipboard mode each result replaces the clipboard when that job completes. A failed job is reported without discarding later queued work.
EchoDraft preserves recognizable speech unless the result is clearly unusable. If a sparse transcript and its retry disagree, the primary transcript is saved in History as needing review instead of being discarded. Insertion mode withholds that uncertain result from automatic paste; clipboard mode keeps a recoverable copy. Empty or punctuation-only output, a detected prompt echo that is too small to trust, and strongly model-generated assistant-style output are still rejected.
On Windows in tap mode, hold Alt while using either configured dictation hotkey to make a Codex prompt. With the current F10 insertion and F9 clipboard bindings, Alt+F10 inserts the finished prompt and Alt+F9 leaves it on the clipboard. Prompt mode uses the same recording and transcription pipeline, then rewrites only for clearer Codex CLI prompting with GPT-5.6 Luna at maximum reasoning effort. It preserves whether the dictation is a standalone request or a contextual follow-up; it does not invent missing context or implementation details. An OpenAI API key is required. History marks these results with a small terminal badge.
- Access: Click the tray icon, then choose View Control Panel. On Windows, EchoDraft opens it on the current virtual desktop and recreates the window only when Windows cannot safely reuse it there.
- Configure: Choose between local and cloud processing
- History: View, search, copy, and delete past transcriptions.
Clean againandMake Codex promptcreate a linked alternative from the stored raw transcript, leave the original unchanged, and copy the alternative without pasting it. You can flag an item as needing correction for Transcription, Cleanup, Prompt, or Paste/delivery and filter locally by that flag. A terminal badge identifies Codex prompt dictations. - To Do: Pending contains mobile memos awaiting action. Archived contains actioned memos and searches every archived row stored on this computer through bounded pages. Both views can copy cleaned or raw text and create eligible copy-only alternatives; Archived does not move items back to Pending.
- Models: Download and manage local Whisper models
- Storage Cleanup: Remove downloaded Whisper models from cache to reclaim space
- Settings: Configure API keys, customize hotkeys, select or test the microphone, preview or adjust dictation sounds, manage permissions, and open Writing for explicit local replacements and fixed application styles. If a selected microphone disconnects or Windows changes its device ID, EchoDraft shows the saved choice as unavailable and temporarily uses the system default. Reselect the microphone to save its current Windows device ID.
- Developer:
Delete logs and transcripts older than 30 dayspreviews one exact cutoff and requires two confirmations before deleting eligible History, every To Do status, linked alternatives/flags, and verified old desktop logs. It excludes captured audio and mobile data.
The private Android companion uses Microsoft sign-in and delegated Files.ReadWrite.AppFolder access to publish into its own Apps/EchoDraft Mobile Inbox OneDrive folder; it does not open a network port on the PC or require a custom backend. In Control Panel → To Do, choose the local PC copy of that folder after OneDrive syncs it. The compact 2×1 widget has a standard microphone and a violet microphone-with-terminal button for Codex prompt mode. Standard phone recordings follow the desktop app's selected transcription provider, model, cleanup setting, and generated-title contract. Prompt recordings use the same transcription path followed by GPT-5.6 Luna at maximum reasoning effort; Android does not transcribe either mode locally.
Mobile results appear only in To Do. They do not paste automatically, replace the clipboard, or create a second History item. The tray shows the pending count and offers Copy newest To Do; that action copies only the newest pending memo and never pastes or submits it. Marked items remain locally searchable under Archived. The floating desktop indicator identifies mobile work as To Do, then shows a brief processed or automatic-retry state when processing finishes instead of leaving stale progress visible. While the dictation renderer is ready, EchoDraft checks the selected sync folder every five seconds and removes an uploaded audio/manifest pair only after the cleaned memo has been saved idempotently to To Do. A bounded processing timeout releases an unresponsive renderer request for retry. The folder can therefore be empty during normal operation; completed memo pairs disappear after consumption. The Android companion is intended for private sideloading, not app-store distribution.
Mobile failures produce a bounded, strictly allowlisted, content-free echodraft-mobile-diagnostics.jsonl support file in the OneDrive app folder when Graph is available. A private local copy is retained and retried if OneDrive is temporarily unavailable; the log excludes dictation/audio, exception messages, paths/URIs, account details, credentials, and phone/device identifiers.
See android/README.md for the no-phone build command, private Microsoft registration, later USB installation, OneDrive connection, microphone-permission step, and widget instructions.
- In-App: Use Settings → General → Local Model Storage → Remove Downloaded Models to clear
~/.cache/echodraft/whisper-models(or%USERPROFILE%\.cache\echodraft\whisper-modelson Windows). - Windows Uninstall: The NSIS uninstaller automatically deletes the same cache directory.
- Linux Packages:
deb/rpmpost-uninstall scripts also remove cached models. - macOS: If you uninstall manually, remove
~/Library/Cachesor~/.cache/echodraft/whisper-modelsif desired.
EchoDraft uses a fixed, model-specific cleanup policy built in the trusted main process. Cleanup is an editing transform only: dictated questions and requests remain text and are never answered or executed.
🛡️ Cleanup contract:
- Adds punctuation, capitalization, quotation marks, and clear formatting
- Corrects spelling, grammar, obvious recognition artifacts, and unambiguous self-corrections
- May consolidate or rewrite locally for clarity while preserving every substantive point
- Keeps questions, requests, caveats, examples, names, numbers, qualifiers, uncertainty, and polarity
- Preserves model identifiers and file, folder, path, directory, and agent-related tokens instead of guessing a cleaner spelling
- Adds quotation marks to explicit speech, not around an entire request merely because it reads like a message
- Requests one JSON object containing a concise content-derived
titleand the complete cleanedtext. EchoDraft only persists the title when that exact contract succeeds. - Keeps usable cleaned text when the title envelope is missing or malformed; contract failure alone never causes fallback to the raw dictation.
- Gives rejected OpenAI BYOK output one model-guided repair on the selected model and effort, using the original transcript, rejected draft, and safety reasons while rechecking the result for fidelity
- Retries suspicious output conservatively and keeps the original wording if preservation cannot be verified; an independently verified dictionary-backed person-name spelling may still be retained
🤖 AI Provider Options:
- OpenAI cleanup: GPT-5.6 Terra, GPT-5.6 Luna, and GPT-5.6 Sol
- OpenAI GPT-5 reasoning: Choose None (recommended default for Luna), Low (more reasoning), or Medium (most thorough) under Settings → AI Text Enhancement. In the current real-audio Luna comparison, None matched Low on usable output quality and was materially faster. A rejected first pass can trigger one model-guided fidelity repair using the same selected model and effort.
- Anthropic: Claude Opus 4.5, Sonnet 4.5, Haiku 4.5
- Google: Gemini 2.5 Pro/Flash/Flash-Lite
- Groq: Ultra-fast Llama and Mixtral inference
- Local: Qwen, LLaMA, Mistral via llama.cpp
📝 Regular Dictation (for normal text):
- "This is just normal text I want transcribed"
- "Meeting notes: John mentioned the quarterly report"
- "Dear Sarah, thank you for your help"
All dictated text is treated as untrusted content to edit. A dictated instruction is never permission to perform a task.
⌨️ Codex Prompt Dictation:
- Produces a ready-to-paste prompt without answering or executing it
- Preserves the requested goal, scope, constraints, uncertainty, and level of detail
- Preserves contextual follow-ups instead of reporting missing information, adding placeholders, or forcing them to stand alone
- Does not guess files, commands, architecture, implementation details, acceptance criteria, or permissions that were not dictated
- Uses GPT-5.6 Luna with maximum reasoning effort, independently of the regular cleanup model and effort settings
Improve transcription accuracy for specific names and technical terms on supported engines:
- Access Settings: Open Control Panel → Settings → Custom Dictionary
- Add Terms: Enter one word, name, identifier, or technical token per entry
- How It Works: EchoDraft sanitizes entries to single lexical terms. For OpenAI transcription, the trusted main process constructs a fixed lexical hint from those terms; supported non-OpenAI engines use their provider-specific hint field. BYOK cleanup receives only the sanitized spellings as trusted lexical data, allowing a tightly bounded final-
irecognition variant to use a listed final-eperson-name spelling only in clear name grammar while the fidelity guard prevents unrelated substitutions. Unsupported transcription providers transcribe without hints, and managed EchoDraft Cloud does not receive the private dictionary.
Examples of words to add:
- Uncommon names (e.g., "Sergey", "Xanthe")
- Technical jargon (e.g., "Kubernetes", "OAuth")
- Brand names (e.g., "EchoDraft", "whisper.cpp")
- Domain-specific terms (e.g., "amortization", "polymerase")
Open Settings → Writing to add an explicit heard phrase → written form correction. These boundary-aware rules are stored locally, remain separate from the speech dictionary, and run before optional cleanup. EchoDraft does not learn rules automatically; each rule can be previewed, edited, disabled, or deleted.
The same page can map an application process name to one fixed Document, Message, or Technical style. Routing uses the process name only. EchoDraft does not capture or send the window title, selection, clipboard contents, screenshot, or surrounding text for this feature. Prompt hotkeys ignore application styles and continue to use GPT-5.6 Luna with maximum reasoning effort.
- EchoDraft Cloud:
- Sign in with Google or email — no API keys needed
- Free plan: 2,000 words/week with 7-day Pro trial for new accounts
- Pro plan: unlimited transcriptions
- Bring Your Own Key (BYOK):
- Use your own API keys from OpenAI, Groq, Mistral, AssemblyAI, or custom endpoints
- Full control over provider and model selection
- Local Processing:
- Install Whisper or NVIDIA Parakeet through the Control Panel
- Download models: tiny (fastest), base (recommended), small, medium, large (best quality)
- Complete privacy - audio never leaves your device
echodraft/
├── main.js # Electron main process & IPC handlers
├── preload.js # Electron preload script & API bridge
├── setup.js # First-time setup script
├── package.json # Dependencies and scripts
├── env.example # Environment variables template
├── CHANGELOG.md # Project changelog
├── src/
│ ├── App.jsx # Main dictation interface
│ ├── main.jsx # React entry point
│ ├── index.html # Vite HTML template
│ ├── index.css # Tailwind CSS v4 configuration
│ ├── vite.config.js # Vite configuration
│ ├── components/
│ │ ├── ControlPanel.tsx # Settings and history UI
│ │ ├── OnboardingFlow.tsx # First-time setup wizard
│ │ ├── SettingsPage.tsx # Settings interface
│ │ ├── ui/ # shadcn/ui components
│ │ │ ├── button.tsx
│ │ │ ├── card.tsx
│ │ │ ├── input.tsx
│ │ │ ├── LoadingDots.tsx
│ │ │ ├── Toast.tsx
│ │ │ ├── toggle.tsx
│ │ │ └── tooltip.tsx
│ │ └── lib/
│ │ └── utils.ts # Utility functions
│ ├── services/
│ │ └── ReasoningService.ts # Multi-provider AI processing (OpenAI/Anthropic/Gemini)
│ ├── utils/
│ └── components.json # shadcn/ui configuration
└── assets/ # App icons and resources
- Frontend: React 19, TypeScript, Tailwind CSS v4
- Build Tool: Vite with optimized Tailwind plugin
- Desktop: Electron 36 with context isolation
- UI Components: shadcn/ui with Radix primitives
- Database: better-sqlite3 for local transcription storage
- Speech-to-Text: OpenAI Whisper (whisper.cpp) + NVIDIA Parakeet (sherpa-onnx) for local, OpenAI API for cloud
- Icons: Lucide React for consistent iconography
npm ci
npm run devThen (recommended before shipping changes):
npm test
npm run typecheck
npm run lintKey docs:
- Debug logging (daily JSONL logs + analysis): see
DEBUG.md - Windows installer build / in-place upgrade: see Building → Windows Install / In-Place Upgrade (and Building the Windows Installer from WSL)
- Windows packaged runtime release gate:
scripts/gate/windows_release_gate.js(safe non-interactive mode by default; use--allow-foreground-automationonly on an idle test desktop) - Windows-specific gotchas:
WINDOWS_TROUBLESHOOTING.md - General troubleshooting:
TROUBLESHOOTING.md
Important stability notes (to keep Windows in-place upgrades working):
- Avoid changing
electron-builder.cjsappId/productNamewithout an explicit migration plan. - Avoid changing the Windows AppUserModelId (
main.jssets this) without understanding taskbar grouping + installer identity implications.
npm run dev- Start development with hot reloadnpm run start- Start production buildnpm run setup- First-time setup (creates .env file)npm run build:renderer- Build the React app onlynpm run download:whisper-cpp- Download whisper.cpp for the current platformnpm run download:whisper-cpp:all- Download whisper.cpp for all platformsnpm run download:llama-server- Download llama.cpp server for local LLM inferencenpm run download:llama-server:all- Download llama.cpp server for all platformsnpm run download:sherpa-onnx- Download sherpa-onnx for Parakeet local transcriptionnpm run download:sherpa-onnx:all- Download sherpa-onnx for all platformsnpm run compile:native- Compile native helpers (Globe key listener for macOS, key listener for Windows)npm run build- Full package build; signs only when certificates are configurednpm run build:mac- macOS package build (distribution requires signing/notarization)npm run build:win- Windows installer/portable build (verify Authenticode before distribution)npm run build:linux- Linux buildnpm run pack- Build without signing (for personal use)npm run dist- Build and package; signs only when certificates are configurednpm run lint- Run ESLintnpm run format- Format code with Prettiernpm run clean- Clean build artifactsnpm run preview- Preview production buildnpm run android:build- Test, lint, and build the private Android companion (no phone required)npm run android:install- Rebuild and install it on exactly one connected, authorized Android phone
The app consists of two main windows:
- Main Window: Minimal overlay for dictation controls
- Control Panel: Full settings and history interface
Both use the same React codebase but render different components based on URL parameters.
- main.js: Electron main process, IPC handlers, database operations
- preload.js: Secure bridge between main and renderer processes
- App.jsx: Main dictation interface with recording controls
- ControlPanel.tsx: Settings, history, and model management
- src/helpers/whisper.js: whisper.cpp integration for local processing
- better-sqlite3: Local database for transcription history
This project uses the latest Tailwind CSS v4 with:
- CSS-first configuration using
@themedirective - Vite plugin for optimal performance
- Custom design tokens for consistent theming
- Dark mode support with
@variant
The build process creates a single executable for your platform:
# Development build
npm run pack
# Production builds
npm run dist # Current platform
npm run build:mac # macOS DMG + ZIP
npm run build:win # Windows NSIS + Portable
npm run build:linux # AppImage + DEBOn Windows, npm run build:win produces two .exe files in dist/:
EchoDraft Setup <version>.exe- NSIS installer (use this to install, reinstall, or upgrade over the top of an existing installation)EchoDraft <version>.exe- portable build (no installer; runs in-place)
Tip: If you're trying to “reinstall/upgrade”, run the Setup installer (NSIS). The portable
.exewon't update an existing installed app.
Security: unsigned Windows builds intentionally disable the in-app automatic updater. Install a locally verified Setup artifact manually; automatic checks, downloads, and installs are enabled only when the build has code signing configured with a pinned publisher.
Windows packaging must run on Windows (native modules like better-sqlite3 are platform-specific and scripts/require-windows.js will block WSL/Linux builds).
If you develop in WSL, copy the repo into the Windows filesystem and run the build from Windows PowerShell:
# From WSL (copy into a Windows folder; keep node_modules/dist out of the copy)
rsync -a --delete \
--exclude ".git" --exclude "node_modules" --exclude "dist" --exclude "resources/bin" \
./ /mnt/c/Users/<you>/AppData/Local/Temp/echodraft-winbuild/# From Windows PowerShell
cd $env:TEMP\echodraft-winbuild
npm ci
npm run build:winOptionally copy the installer somewhere convenient (e.g. Downloads) for an in-place upgrade:
Copy-Item ".\\dist\\EchoDraft Setup *.exe" "$env:USERPROFILE\\Downloads\\"Note: build/pack/dist scripts automatically download whisper.cpp, llama-server, and sherpa-onnx for the current platform. For multi-platform packaging from one host, run the :all variants first (npm run download:whisper-cpp:all, npm run download:llama-server:all, npm run download:sherpa-onnx:all).
Create a .env file in the root directory (or use npm run setup):
# OpenAI API Configuration (optional - only needed for cloud processing)
OPENAI_API_KEY=your_openai_api_key_here
# Optional: Customize the Whisper model
WHISPER_MODEL=whisper-1
# Optional: Set language for better transcription accuracy
LANGUAGE=
# Optional: Anthropic API Configuration
ANTHROPIC_API_KEY=your_anthropic_api_key_here
# Optional: Google Gemini API Configuration
GEMINI_API_KEY=your_gemini_api_key_here
# Optional: Groq API Configuration (ultra-fast inference)
GROQ_API_KEY=your_groq_api_key_here
# Optional: Mistral API Configuration (Voxtral transcription)
MISTRAL_API_KEY=your_mistral_api_key_here
# Optional: Debug mode
DEBUG=falseFor local processing, EchoDraft uses OpenAI's Whisper model via whisper.cpp - a high-performance C++ implementation:
- Bundled Binary: whisper.cpp is bundled with the app for all platforms
- GGML Models: Downloads optimized GGML models on first use to
~/.cache/echodraft/whisper-models/ - No Dependencies: No Python or other runtime required
System Fallback: If the bundled binary fails, install via package manager:
- macOS:
brew install whisper-cpp - Linux: Build from source at https://github.com/ggml-org/whisper.cpp
From Source: When running locally (not a packaged build), download the binary with npm run download:whisper-cpp so resources/bin/ has your platform executable.
Requirements:
- Sufficient disk space for models (75MB - 3GB depending on model)
Upgrading from Python-based version: If you previously used the Python-based Whisper, you'll need to re-download models in GGML format. You can safely delete the old Python environment (~/.echodraft/python/) and PyTorch models (~/.cache/whisper/) to reclaim disk space.
EchoDraft also supports NVIDIA Parakeet models via sherpa-onnx - a fast alternative to Whisper:
- Bundled Binary: sherpa-onnx is bundled with the app for all platforms
- INT8 Quantized Models: Efficient CPU inference
- Models stored in:
~/.cache/echodraft/parakeet-models/
Available Models:
parakeet-tdt-0.6b-v3: Multilingual (25 languages), ~680MB
When to use Parakeet vs Whisper:
- Parakeet: Best for speed-critical use cases or lower-end hardware
- Whisper: Best for quality-critical use cases or when you need specific model sizes
- Hotkey: Change in the Control Panel (default: backtick `) - fully customizable
- Tray Status: Click the tray icon for current dictation state, last dictation copy, and Control Panel access
- Processing Method: Choose local or cloud in Control Panel
- Whisper Model: Select quality vs speed in Control Panel
- UI Theme: Edit CSS variables in
src/index.css - Window Size: Adjust dimensions in
main.js - Database: Transcriptions stored in user data directory
We welcome contributions! Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Run
npm run lintbefore committing - Follow the existing code style
- Update documentation as needed
- Test on your target platform before submitting
EchoDraft is designed with privacy and security in mind:
- Local Processing Option: Keep your voice data completely private
- No Analytics: We don't collect any usage data or telemetry
- Optional Local Debug Logs: You can enable a high-detail debug log that is written locally to disk (never uploaded automatically)
- Open Source: All code is available for review
- Secure Storage: API keys are stored securely in your system's keychain/credential manager
- Minimal Permissions: Only requests necessary permissions (microphone, accessibility)
- Microphone permissions: Grant permissions in System Preferences/Settings
- Accessibility permissions (macOS): Required for automatic text pasting
- Go to System Settings → Privacy & Security → Accessibility
- Add EchoDraft and enable the checkbox
- Use "Fix Permission Issues" in Control Panel if needed
- API key errors (cloud processing only): Ensure your OpenAI API key is valid and has credits
- Set the key through the Control Panel or an environment variable
- EchoDraft deliberately redacts credentials from diagnostic logs
- Local Whisper issues:
- whisper.cpp is bundled with the app
- If bundled binary fails, install via
brew install whisper-cpp(macOS) - Check available disk space for models
- Global hotkey conflicts: Change the hotkey in the Control Panel - any key can be used
- GNOME Wayland: Hotkeys are registered via gsettings; check Settings → Keyboard → Shortcuts for conflicts
- Text not pasting:
- macOS: Check accessibility permissions (System Settings → Privacy & Security → Accessibility)
- Linux X11: Install
xdotool - Linux Wayland: Install
wtypeorydotoolfor paste simulation (ensureydotoolddaemon is running) - All platforms: Text is always copied to clipboard - use Ctrl+V (Cmd+V on macOS) to paste manually
- Panel position: If the panel appears off-screen, restart the app to reset position
- Microphone uncertainty: Open Settings → General → Microphone and run the 15-second level test. It starts only when clicked and releases the microphone when stopped, completed, or closed.
- Slow cloud transcription or cleanup: After ten seconds EchoDraft shows the elapsed stage time. You can cancel the active request, including supported in-flight cleanup calls and retry delays. Transient network, timeout, rate-limit, and server failures receive at most one sequential retry, and cancelled text is never delivered later.
If you need to diagnose lag, missing audio, or truncated transcripts, enable Debug mode in the Control Panel:
- Control Panel → Developer → Debug mode
- Logs are written as JSONL (one JSON object per line) to a daily file:
- Preferred:
logs/echodraft-debug-YYYY-MM-DD.jsonlnext to the installedEchoDraft.exe - Fallback: the app’s
userData/logsdirectory (if the install directory isn’t writable)
- Preferred:
- Use Open Logs Folder in the Developer section to jump to the current log location.
- Use Delete Diagnostic Data to permanently remove EchoDraft daily logs and rolling input-audio captures from its verified log locations. Source audio is retained for completed desktop and mobile dictations even when debug logging is off. Turn debug mode off first if you do not want a fresh log created.
- Use Delete logs and transcripts older than 30 days for the separate age-based flow. It uses two cancel-default confirmations and deletes only previewed History, every To Do status, their linked alternatives/flags, and verified desktop JSONL logs strictly older than one captured UTC cutoff. It never deletes captured audio or mobile data.
For full details (enable/disable options, log format, and what gets captured), see DEBUG.md.
- The first line of each file is a
type: "header"record (system/app details + settings snapshot placeholders). Most queries should filter it out. - When debug mode is enabled, the renderer logs a
Renderer settings snapshotand traces localStorage mutations (localStorage.setItem/removeItem/clear) with basic redaction heuristics. - Follow a single dictation end-to-end via
meta.sessionIdand/ormeta.jobId(these appear on hotkey/dictation/paste/history events). - Useful
scopevalues to filter by:hotkey,dictation,audio,streaming,transcription,reasoning,paste,history,settings,performance,pipeline. - To separate “transcription vs cleanup” issues:
- Compare
rawTextvscleanedTextintracelogs (e.g.Transcription result text,Streaming transcript text,Dictation transcript text). - In History,
raw_textis stored separately and shown under “Details” when different. - When EchoDraft Cloud cleanup runs,
meta.sourceis tagged asechodraft-reasoned(orechodraft-byok-reasoned).
- Compare
If you have jq installed, a few quick recipes:
# 1) Show warnings/errors (good first-pass triage)
jq -r 'select(.type!="header") | select(.level=="warn" or .level=="error") | [.ts,.scope,.message] | @tsv' \
logs/echodraft-debug-YYYY-MM-DD.jsonl
# 2) Hotkey → recording latency (helps diagnose “delay before recording starts”)
jq -r 'select(.message=="Dictation recording started") | [.ts,.meta.hotkeyToRecordingMs,.meta.method] | @tsv' \
logs/echodraft-debug-YYYY-MM-DD.jsonl
# 3) Pipeline timings (helps spot truncation, slow cleanup, etc.)
jq -r 'select(.message=="Pipeline timing") | [.ts,.meta.mode,.meta.model,.meta.roundTripDurationMs,.meta.transcriptionProcessingDurationMs,.meta.reasoningProcessingDurationMs] | @tsv' \
logs/echodraft-debug-YYYY-MM-DD.jsonlWarning: Debug logs may contain transcribed text and other sensitive data. Share only with trusted support.
- Check the Issues page
- Review the console logs for debugging information
- For local processing: Ensure whisper.cpp is accessible and models are downloaded
- For cloud processing: Verify your OpenAI API key and billing status
- Check the Control Panel for system status and diagnostics
- Local Processing: Use "base" model for best balance of speed and accuracy
- Cloud Processing: Generally faster but requires internet connection
- Model Selection: tiny (fastest) → base (recommended) → small → medium → large (best quality)
- Permissions: Ensure all required permissions are granted for smooth operation
Q: Is EchoDraft really free? A: Yes! EchoDraft is open source and free to use. The free plan includes 2,000 words/week of cloud transcription, and local processing is completely free with no limits. Pro plan ($9/month) offers unlimited cloud transcription.
Q: Which processing method should I use? A: Use local processing for privacy and offline use. Use cloud processing for speed and convenience.
Q: Can I use this commercially? A: Yes! The MIT license allows commercial use.
Q: How do I change the hotkey? A: Open the Control Panel (right-click tray icon) and go to Settings. You can set any key as your hotkey.
Q: Is my data secure? A: With local processing, your audio never leaves your device. With cloud processing, audio is sent to OpenAI's servers (see their privacy policy).
Q: What languages are supported? A: EchoDraft supports 58 languages including English, Spanish, French, German, Chinese, Japanese, and more. Set your preferred language in the .env file or use auto-detect.
EchoDraft is actively maintained and ready for production use. Current version: 1.4.10
- ✅ Core functionality complete
- ✅ Cross-platform support (macOS, Windows, Linux)
- ✅ EchoDraft Cloud with account system and usage tracking
- ✅ Free and Pro plans with Stripe billing
- ✅ Local and cloud processing
- ✅ Multi-provider AI (OpenAI, Anthropic, Gemini, Groq, Mistral, Local)
- ✅ Compound hotkey support
- ✅ Windows Push-to-Talk with native key listener
- ✅ Custom dictionary for improved transcription accuracy
- ✅ NVIDIA Parakeet support via sherpa-onnx
- ✅ GNOME Wayland native global shortcuts
- OpenAI Whisper - The speech recognition model that powers both local and cloud transcription
- whisper.cpp - High-performance C++ implementation of Whisper for local processing
- NVIDIA Parakeet - Fast ASR model for efficient local transcription
- sherpa-onnx - Cross-platform ONNX runtime for Parakeet model inference
- Electron - Cross-platform desktop application framework
- React - UI component library
- shadcn/ui - Beautiful UI components built on Radix primitives
- llama.cpp - Local LLM inference for AI-powered text processing