Open-source, local-first voice dictation. Hold a key, speak, release — clean, formatted text appears at your cursor in any app. Windows · Linux · macOS.
Your voice never leaves your machine: a local speech-to-text model (Parakeet / Whisper) does the transcription, and a tiny local LLM strips the "um"s, fixes punctuation, and formats the result — all in a few hundred milliseconds. No cloud, no account, no telemetry.
Black & lavender. A quiet night garden instead of a cloud.
Windows 10/11 (64-bit): ⬇ Download the Wisteria installer
Just download and run it — no extra steps, no build tools required. The installer sets up the app, adds the WebView2 runtime if needed, and offers to install the optional local formatter (Ollama). On first launch, Wisteria downloads its speech-to-text model automatically.
Linux (x86_64) — one command:
curl -fsSL https://raw.githubusercontent.com/dev-rjav/Wisteria/main/install.sh | bashThis downloads the prebuilt AppImage (one portable file that runs on virtually any distro),
installs it to ~/.local/bin, adds an application-menu launcher, and offers to set up Ollama. No
compiling, no root required. Uninstall with the same command plus -s -- --uninstall. You can also
grab the .AppImage or .deb directly from the Releases page.
Prefer to browse versions and release notes? See the Releases page. A macOS build is on the roadmap.
Most polished dictation tools stream your voice to proprietary models on rented GPUs. Wisteria runs the whole pipeline on your machine:
global hotkey ▶ warm mic ▶ VAD ▶ local ASR model ▶ local small-LLM cleanup ▶ paste at cursor
- 100% offline by default. Nothing is uploaded; there is no account and no telemetry.
- Cross-platform, first-class. Windows, Linux, and macOS are all supported — OS-specific code is isolated in per-platform modules.
- Open models, swappable. Pick your ASR and formatter models like picking a theme.
- Optional BYOK cloud. You can point the formatter at your own API key for cloud-grade polish, but it is never required and never the default.
Two local model stages (see models-research.md for the full landscape and our default picks):
- Stage A — Transcription (ASR): pluggable local speech-to-text. Default Parakeet TDT 0.6B v3 (realtime English/European), with Whisper large-v3-turbo for 99-language mode and Moonshine for low-end hardware.
- Stage B — Formatting (LLM): a very small, fast local model (default Qwen3 family, run via Ollama) that removes fillers and false starts, fixes punctuation/capitalization, and shapes the text for the target app. Every stage falls back to the raw transcript if the formatter is unavailable — dictation never blocks on it.
The formatter is entirely optional: with cleanup intensity set to Off, the LLM stage is skipped and the raw transcript is pasted directly.
- Push-to-talk global hotkey (hold to talk), configurable per OS (default F8).
- Hands-free lock — double-tap the hotkey to keep recording without holding; tap again to finish.
- Warm mic capture (
cpal), auto-selecting a real input device and skipping system-loopback endpoints. - Paste-at-cursor delivery via simulated Ctrl/⌘+V with clipboard save/restore.
- Cleanup intensity — Off / Light / Medium / High, plus per-behavior Transforms (auto-punctuation, filler removal, smart capitalization, email/number formatting) that physically compose the formatter prompt.
- Writing styles — Concise (faithful cleanup) / Professional / Casual / Detailed.
- Personal dictionary — teach it your names, jargon, and brands; a conservative deterministic matcher (Soundex + Jaro-Winkler) fixes sound-alikes even when the LLM is off, with import/export.
- Voice snippets — say a keyword + trigger phrase to expand verbatim text.
- Ask AI mode (opt-in) — open a dictation with a keyword and the spoken request is answered by the local LLM and the answer is pasted instead of a transcript.
- BYOK cloud formatter (optional) — link an OpenAI-compatible API service (OpenRouter, OpenAI,
Groq, …) in Settings and use a hosted model for cleanup / Ask AI instead of local Ollama. Add,
edit, and remove services and fetch their model lists from the app. Local stays the default; your
key lives only in your local config. For the lowest latency, pick a small, fast, non-reasoning
model (e.g.
gpt-4o-mini, or Groq'sllama-3.1-8b-instant) — Wisteria keeps the HTTPS connection warm between dictations and, on OpenRouter, routes to the highest-throughput backend automatically. - History window with per-entry copy and real day grouping.
- The Dock — a small, frameless, always-on-top overlay at the bottom of the screen that renders a flowing lavender wave with your voice (idle → listening → processing → done).
A Cargo workspace of three crates plus the frontend assets:
Wisteria/
├── Cargo.toml # Workspace manifest (shared deps, release profile)
├── README.md # This file
├── models-research.md # ASR + formatter model landscape and our default picks
└── crates/
├── wisteria-core/ # Headless dictation pipeline (the library)
│ └── src/
│ ├── lib.rs # Crate root; composes the stages, TARGET_SAMPLE_RATE
│ ├── config.rs # User config (config.toml): hotkey, models, transforms, styles…
│ ├── models.rs # Download + verify model artifacts (gitignored, fetched on first run)
│ ├── audio.rs # Warm cpal capture → 16 kHz mono f32
│ ├── hotkey.rs # Global push-to-talk listener (rdev) + double-tap lock machine
│ ├── asr.rs # Warm Parakeet/Whisper engine (transcribe-rs / ONNX Runtime)
│ ├── format.rs # Optional LLM cleanup + Ask AI (Ollama), prompt composition
│ ├── dictionary.rs # Deterministic custom-vocabulary matcher (Soundex + Jaro-Winkler)
│ ├── snippets.rs # Keyword-gated voice text expansion
│ ├── paste.rs # Clipboard save → set → synthetic paste → restore
│ └── engine.rs # Restartable pipeline that the CLI/GUI drive on worker threads
├── wisteria-cli/ # Headless daemon — the console is the UI (Phase 1)
│ └── src/main.rs
└── wisteria-gui/ # Tauri v2 desktop app (workspace window + Dock overlay)
├── src/main.rs # Tauri commands, tray, single-instance, window lifecycle
├── src/ollama.rs # Local Ollama HTTP client (list + stream-pull formatter models)
├── tauri.conf.json # Window/bundle config (main window + transparent Dock)
├── capabilities/ # Tauri capability allowlist
└── dist/ # Frontend (index.html, dock.html, app.js, dock.js, wave.js, styles.css)
| Module | Responsibility |
|---|---|
config |
Persist/load config.toml in the platform app-data dir; forward-compatible defaults |
models |
Ensure ASR artifacts are downloaded and verified (never committed) |
audio |
Keep the mic stream warm; downmix + resample to 16 kHz mono; drop <300 ms clips |
hotkey |
Consume the PTT key globally (grab on Win/macOS, listen on Linux); PTT + hands-free lock |
asr |
Load the ASR engine once and keep it resident so latency is just inference |
format |
Compose the cleanup prompt from transforms/style/dictionary; call Ollama; Ask AI |
dictionary |
Conservative, always-on correction of custom vocabulary |
snippets |
Expand <keyword> <trigger> spoken phrases to verbatim text |
paste |
Deliver text without losing the user's clipboard |
engine |
Own the recorder, ASR, and formatter on worker threads; hot-reload on config change |
Config lives at %LOCALAPPDATA%/WisteriaData/config.toml (Windows),
~/.local/share/WisteriaData/config.toml (Linux), or
~/Library/Application Support/WisteriaData/config.toml (macOS) — a folder kept separate from the
installed program files so uninstalling or updating never touches your settings/history. Model weights are
downloaded on first run and are never committed.
Prerequisites
- Rust (stable). On Windows, the MSVC toolchain + VS Build Tools (C++) are required for linking.
- Tauri v2 prerequisites for your OS (WebView2 on
Windows;
webkit2gtketc. on Linux). - Ollama running locally if you want the LLM cleanup / Ask AI stages
(e.g.
ollama pull qwen3:0.6b). Skip it to run transcription-only.
Headless daemon (CLI)
cargo run -p wisteria-cliThe console is the UI: it loads config, downloads and warms the models, then arms the push-to-talk loop. Hold the hotkey, speak, release — the transcript is pasted into the focused app.
Desktop app (GUI)
# with the Tauri CLI installed (cargo install tauri-cli --version '^2')
cargo tauri dev --config crates/wisteria-gui/tauri.conf.json
# or run the binary directly
cargo run -p wisteria-guiJust want to use Wisteria? Grab the prebuilt installer from Download above — you don't need any of this. This section is for building the installer yourself.
Wisteria ships as a single standalone GUI installer (.exe) built with Tauri's NSIS bundler.
End users need no Rust, MSVC, or build tools — only the build machine does. The installer:
- ships the prebuilt app and auto-installs the WebView2 runtime if it's missing;
- installs per-user (no admin / UAC prompt);
- after install, prompts to install Ollama for the optional local AI formatter model — decline
and Wisteria still runs transcription-only (see
crates/wisteria-gui/installer/hooks.nsh).
Parakeet (speech-to-text) and the formatter model are fetched on first run / from inside the app, so the installer stays small (~10 MB).
Build it (on a Windows machine with the prerequisites above + cargo install tauri-cli --version '^2'):
pwsh ./scripts/build-windows-installer.ps1
# → target/release/bundle/nsis/Wisteria_<version>_x64-setup.exeor directly:
cd crates/wisteria-gui
cargo tauri build --bundles nsismacOS and Linux installers (
.dmg,.AppImage/.deb) come from the samecargo tauri buildon those OSes and are tracked for a later phase.
Everything is editable in the GUI, and persisted to config.toml. Highlights:
ptt_key— push-to-talk key or+-separated combo (defaultF8).model— ASR model identifier.format— cleanup intensity (off/light/medium/high).style— writing voice (concise/professional/casual/detailed).transforms— per-behavior toggles (all on by default).formatter_url/formatter_model/formatter_timeout— the local Ollama endpoint + model.formatter_backend— which backend is active: empty/local(Ollama) or an API service id.api_providers— linked OpenAI-compatible cloud services (name, base URL, key, model) for BYOK.dictionary— custom vocabulary words.snippets+snippet_keyword— voice text expansions.ask_ai_enabled+ask_ai_keyword— opt-in Ask AI mode.
- Phase 0 — research, name/brand, repo scaffold ✅
- Phase 1 — MVP: hotkey → record → transcribe → paste ✅
- Phase 2 — Dock overlay with wave animation; hands-free mode; cancel ✅
- Phase 3 — Formatter LLM stage + cleanup intensity + transforms + styles ✅
- Phase 4 — History, dictionary + auto-learn, model manager ✅ (auto-learn in progress)
- Phase 5 — Context awareness (active-app detection → tone), richer command mode
Cross-platform is non-negotiable — no feature is merged if it only works on one OS unless it's
explicitly stubbed with a tracked TODO for the others. Local-first is non-negotiable — cloud (BYOK)
is always optional, never required, never default. Commits follow
Conventional Commits (feat:, fix:, docs:, chore:,
refactor:) and stay small and atomic.
Dual-licensed under MIT OR Apache-2.0, at your option.