Architecture
What's inside the app, and how the parts talk to each other.
High-level design
VoxCtrl is a Tauri 2 application: a compiled Rust backend that spawns a WebView window running a Svelte 5 SPA (styled with Tailwind CSS 4, bundled with Vite 5). The two halves communicate over Tauri's IPC bridge — invoke commands from the frontend, event emitters from the backend.
The backend also spawns a native overlay helper process (voxctrl-overlay, built with Slint)
that renders the always-on-top, click-through recording HUD. The backend streams newline-delimited JSON
(status / position / shutdown) to the helper's stdin; the helper runs
its own 16ms animation loop, drives spring-based load/unload animations, and builds the per-style visualizer
geometry (oscilloscope trace, radar sweep, ocean waves, VU LED matrix) on every tick.
Crate workspace
The backend is a Cargo workspace of focused, single-responsibility crates:
src-tauri/— Tauri app entry + IPC command handlers (lib.rsis the pipeline coordinator,commands.rsholds the#[command]handlers,state.rsholds the sharedAppState)voxctrl-config—AppConfigstruct, TOML/JSON persistencevoxctrl-audio— microphone capture, resampling, VU metervoxctrl-hotkeys— global key listener (evdev / Win32)voxctrl-inference— Whisper transcription + post-processingvoxctrl-routing— output target + hotkey binding data models, routervoxctrl-inject— text injection via wtype/xdotool/clipboardvoxctrl-tts— Piper/Pocket-TTS/Espeak TTS enginevoxctrl-mcp— MCP JSON-RPC server (Unix socket / named pipe)voxctrl-dbus— DBus service (Linux session bus)voxctrl-llm— OpenAI-compatible LLM HTTP client
Data flow
A hotkey press is picked up by voxctrl-hotkeys and sent over a gesture_tx channel
to the lib.rs coordinator, which starts the audio recorder and determines the target from the
binding. Audio chunks accumulate in a buffer until hotkey release or VAD-detected silence, at which point
the buffer is handed to InferenceEngine.process() in voxctrl-inference, which runs
(in order): noise gate (VAD), Whisper transcription, filler removal, spoken punctuation, auto-format lists,
snippet expansion, custom vocabulary fuzzy correction, code mode, a silence hallucination filter, and —
optionally, per binding — an LLM rewrite via the OpenAI-compatible API.
The resulting text is sent to OutputTargetRouter.route() in voxctrl-routing, which
dispatches by delivery type: inject → voxctrl-inject, clipboard →
arboard, file → tokio::fs, http/webhook → reqwest,
exec → std::process, socket → UnixStream,
dbus → voxctrl-dbus, mcp → the MCP response queue, and
pipe → a named FIFO. A Tauri event then notifies the frontend (status tick, history update).
Concurrency model
VoxCtrl uses Tokio for async I/O plus dedicated OS threads for latency-sensitive work:
- Main Tauri thread (OS thread) — window management, IPC dispatch
- Audio capture (OS thread, cpal) — microphone streaming at hardware rate
- Audio level emitter (Tokio task) — forwards RMS levels to the UI every ~50ms
- Hotkey listener (OS thread) — evdev/Win32 event loop
- Inference worker (OS thread) — blocking Whisper computation; the
WhisperState(KV cache + attention buffers) is allocated once at load and reused across all calls - Status ticker (Tokio task) — emits
status-tickevents every 250ms - Config watcher (Tokio task) — inotify/kqueue on the config files
- MCP server (Tokio task) — Unix socket accept loop
- DBus service (Tokio task) — session bus method handler
- TTS FIFO watcher (Tokio task) — named pipe reader for TTS input
Shared state is an Arc<AppState>, using AtomicBool/AtomicU32
for hot-path flags and Mutex for heavier data (targets, history, TTS handle). Channels
(crossbeam/tokio) carry typed messages between stages: audio_tx/audio_rx
(Vec<f32> chunks), inference_tx/inference_rx
(InferenceRequest), text_tx/text_rx (InferenceOutput),
audio_level_tx/level_rx (f32 RMS), and
gesture_tx/gesture_rx (GestureEvent), plus a
hotkey_reloader channel that pushes updated bindings to the listener thread for hot-reload.
Frontend architecture
The Svelte frontend is a single-page app with three logical "pages" rendered as separate Tauri windows:
- /settings — Settings component, a sidebar with 9 tabs: General, Engine, Routing (targets + bindings editor), Visual, Audio, TTS, Features, OpenAI API, and About
- /overlay — Overlay component, the web overlay layer; the on-screen HUD for built-in
styles is rendered by the native
voxctrl-overlayhelper. The eight built-in styles are: BlueWave/Ocean Wave (default), VoiceCard/Voice Card, Waveform, Pulse/Pulse Ring, MonoBars/Mono Bars, Spectrum/Neon Spectrum, Terminal/Retro Terminal, and Vinyl/Analog VU - /history — History component
State management: src/stores/config.ts holds a reactive AppConfig with a
400ms-debounced auto-save via the save_config IPC command, and listens for
config-changed events. src/stores/status.ts tracks live state from
status-tick events plus derived stores (recording, speaking,
wordCount, activeTargetLabel).
File locations
| Path | Contents |
|---|---|
| ~/.config/voxctrl/config.json | Main application config |
| ~/.config/voxctrl/targets.toml | Output target definitions |
| ~/.config/voxctrl/bindings.toml | Hotkey binding definitions |
| ~/.local/share/voxctrl/models/ | Downloaded Whisper GGUF models |
| ~/.local/share/voxctrl/piper-voices/ | Downloaded Piper voice packs |
| /tmp/voxctrl-mcp.sock | MCP Unix domain socket (Linux) |
| \\.\pipe\voxctrl-mcp | MCP named pipe (Windows) |