Skip to content

Repository files navigation

voiceflow

Press a hotkey. Speak. Your words appear in whatever window has focus.

Local, GPU-accelerated voice dictation for Linux — a free, open-source alternative to paid dictation apps, built for the platform they all skipped: GNOME on Wayland. Whisper runs on your hardware. No cloud, no subscription, no audio ever leaving your machine.

tests license: MIT python 3.13 platform

live preview while dictating

The on-screen preview while you speak — it updates every second and never steals focus. Release the hotkey and the final, full-context transcription is pasted where your cursor is.

Install

Pick your operating system — the commands are not interchangeable.

🐧 Linux (Ubuntu, GNOME on Wayland)

Open a terminal and run:

curl -fsSL https://raw.githubusercontent.com/AveJaPl/voiceflow/main/install.sh | bash

The only step that asks for sudo installs ydotool + a udev rule (the script explains why). Updating later: run the same command again.

Prefer to read before you run? curl -fsSL …/install.sh > install.sh, open it, then bash install.sh.

🪟 Windows 10 / 11 (beta)

Easiest — no terminal at all:

  1. Download voiceflow-install.bat from the latest release (Assets section)
  2. Double-click it; if SmartScreen warns, choose More info → Run anyway

Or from a terminal — this exact command works in both PowerShell and cmd, from any drive or directory:

powershell -NoProfile -ExecutionPolicy Bypass -Command "irm https://raw.githubusercontent.com/AveJaPl/voiceflow/main/windows/install.ps1 | iex"

Updating later: same file / same command again. A signed .exe installer is planned and will appear on the Releases page when ready. Details and current limitations: docs/WINDOWS.md.

⚠ Do not run the Linux command on Windows: if WSL is installed, | bash silently executes it inside WSL, where dictation cannot work (the installer now detects this and stops, but save yourself the round trip).

After installing (both systems)

The installer downloads the speech model (~1.6 GB) with a visible progress bar as its final step — so once it says done, everything works immediately. Press the hotkey (Super+G on Linux, Ctrl+Shift+Space on Windows), speak, press it again. The text lands in whatever window has focus.

Manual install from source (Linux)
git clone https://github.com/AveJaPl/voiceflow && cd voiceflow
sudo bash scripts/install-system-deps.sh   # ydotool, wl-clipboard, python3-gi-cairo, udev
uv sync                                    # pinned Python 3.13 + dependencies
mkdir -p ~/.local/bin
printf '#!/usr/bin/env bash\nexec "%s/.venv/bin/voiceflow" "$@"\n' "$PWD" > ~/.local/bin/voiceflow
chmod +x ~/.local/bin/voiceflow
systemctl --user enable --now ydotool.service
cp systemd/voiceflow.service ~/.config/systemd/user/
systemctl --user daemon-reload && systemctl --user enable --now voiceflow.service
bash scripts/install-hotkey.sh             # Super+G by default

Why this exists

Dictating prompts to AI assistants beats typing them — but the good dictation apps are subscription cloud services, and none of them run on Linux at all. voiceflow does the same job with a local Whisper model:

voiceflow typical paid dictation app
price free, MIT ~$15/month
audio leaves your machine never always
Linux / GNOME / Wayland native unsupported
latency after you stop speaking ~0.1 s on a GPU network round-trip
works offline yes no

Features

  • Fast. A user daemon keeps the model warm in VRAM: on an RTX 3070, large-v3-turbo transcribes 4 s of speech in ~0.04 s. No GPU? It falls back to CPU (int8) automatically — slower, still private.
  • Live preview. A minimal always-on-top card shows what the model hears while you are still talking, with a pulsing recording indicator. The pasted text is a second, full-context pass — accuracy is never sacrificed for the preview.
  • Voice-chat aware. Dictating while on Discord? Your mic stream to the call is muted (they never hear your prompts) and the call's audio is ducked to 40% (it stops derailing your sentence). Both restored the instant recording ends; your manual mute is respected.
  • Any language. Whatever Whisper speaks — set model.language (en, de, pl, … or null to auto-detect).
  • Custom vocabulary. Your product names and jargon, biased into the decoder so it stops mangling them (model.vocabulary). It biases only — never rewrites your words.
  • Honest with your clipboard. Text is injected via paste (the only non-ASCII-safe path on GNOME Wayland) and your previous clipboard is put back afterwards.
  • Tested without hardware. 82 tests, none need a GPU, microphone, or display — they run in CI on every commit.

Requirements

OS Linux with PipeWire (developed on Ubuntu 26.04) · Windows 10/11 betadocs/WINDOWS.md
Desktop GNOME on Wayland (developed on GNOME 50) — other compositors: see roadmap
System packages ydotool, wl-clipboard, python3-gi-cairo (the last package enables charts in the GTK application)
GPU optional; NVIDIA with ~2.5 GB free VRAM for large-v3-turbo (CUDA libraries come from pip — no CUDA Toolkit install needed)
Disk ~1.6 GB model + ~2.7 GB environment

Usage

dictate Super+G → speak → Super+G
cancel without pasting voiceflow cancel
health check & diagnostics voiceflow status
logs journalctl --user -u voiceflow -f
change the hotkey VOICEFLOW_BINDING='<Control><Alt>space' bash scripts/install-hotkey.sh
free the VRAM (before gaming) systemctl --user stop voiceflow
stress-test / leak check bash scripts/audit.sh

Configuration

~/.config/voiceflow/config.yaml — created with commented defaults on first run; restart the daemon after editing.

model:
  name: large-v3-turbo    # any faster-whisper model
  device: cuda            # cuda | cpu | auto
  language: pl            # ISO 639-1; null = auto-detect
  vocabulary: []          # names the decoder should lean towards
inject:
  method: clipboard       # clipboard | ydotool | auto
  paste_key: ctrl+shift+v # terminals paste with shift; GUI apps may want ctrl+v
mute_apps:
  apps: [WEBRTC VoiceEngine]   # Discord's mic stream; find others via pw-dump
  duck_volume: 0.4        # duck the call to 40% while dictating
Full configuration reference
key default meaning
model.name large-v3-turbo any faster-whisper model id
model.device cuda cuda / cpu / auto; CUDA failure falls back to CPU
model.compute_type float16 precision on GPU; CPU uses int8
model.language pl ISO 639-1 code or null for auto-detect
model.beam_size 5 decoder beam width
model.vocabulary [] terms biased into decoding
audio.source null PipeWire source; null = default mic
audio.max_seconds 300 safety cap on one recording
inject.method clipboard ydotool types ASCII only; clipboard is safe for all languages
inject.paste_key ctrl+shift+v the paste chord that gets sent
inject.restore_clipboard true put the previous clipboard back
preview.enabled true live preview while speaking
preview.interval_seconds 1.0 preview refresh rate
mute_apps.enabled true mute configured apps' mic streams while recording
mute_apps.apps [WEBRTC VoiceEngine] PipeWire application.name values
mute_apps.duck_enabled true also duck those apps' playback
mute_apps.duck_volume 0.4 duck target as a fraction of full volume
mute_apps.duck_rules {} per-application playback targets keyed by PipeWire application.name
presence.enabled false show Discord Rich Presence while dictating
presence.client_id "" Discord developer application ID used for Rich Presence
overlay.enabled true the on-screen indicator card

The config file is generated once and not migrated — delete it to regenerate with current defaults.

How it works

hotkey ─▶ voiceflow toggle ──unix socket──▶ voiceflow daemon (systemd --user)
                                             ├─ recorder     pw-record, 16 kHz WAV
                                             ├─ transcriber  faster-whisper, warm in VRAM
                                             ├─ preview      re-transcribes the tail every 1 s
                                             ├─ overlay      separate GTK3 process (X11 popup)
                                             ├─ micmute      mutes/ducks voice chats (wpctl)
                                             └─ injector     wl-copy + paste keystroke

A thin client (~0.1 s startup) talks to a persistent daemon, so the model loads once per login, not once per dictation. The modules do not import each other — the daemon composes them — which is what keeps the test suite hardware-free and the platform ports tractable.

Why GNOME/Wayland needed all this (field notes)

Constraints discovered the hard way — read this before porting:

  • You cannot type into another window. Wayland forbids input injection and GNOME implements no virtual-keyboard protocol, so wtype is out. ydotool works at the kernel level via /dev/uinput, but its type command silently drops non-ASCII characters. Hence: clipboard + a real paste keystroke, clipboard restored after.
  • A window cannot refuse focus — a normal preview window would swallow the paste. The overlay is an X11 override-redirect popup via XWayland: the one window type the compositor neither focuses nor repositions.
  • pw-record exits 1 after SIGINT even when the WAV is complete. Success must be judged by parsing the WAV header, not the exit code.
  • wl-copy forks, and its child owns the Wayland selection; piping its stdio and waiting for EOF deadlocks every paste. No pipes to self-daemonizing processes.
  • User services race the login. From default.target the daemon gets no DISPLAY/XAUTHORITY; the unit binds to graphical-session.target, with a runtime fallback that locates Mutter's XWayland auth cookie.

Roadmap

Ordered by how much of the codebase carries over — the daemon, transcriber, preview logic, vocabulary, and config are already platform-neutral; only recorder, injector, overlay, and micmute touch the OS:

  • Native settings app (GTK4/libadwaita) — status, model & language, vocabulary editor, voice-chat ducking, all without touching YAML (in progress)
  • Other Wayland compositors (KDE, Hyprland, Sway) — easiest port: they implement virtual-keyboard/layer-shell, so injection and overlay get simpler (#1)
  • Prebuilt packages — .deb, AUR, Flatpak/AppImage (the /dev/uinput access needs design work in sandboxed formats) (#4)
  • Windows (beta) — same core, Win32 backends: in-daemon global hotkey, PortAudio recording, clipboard+SendInput injection, no-focus tkinter overlay, Core Audio ducking. See docs/WINDOWS.md; feedback in #2
  • macOS — AVFoundation recorder, CGEvent paste, NSPanel overlay, Accessibility permissions; Apple Silicon inference via CPU or an mlx/whisper.cpp backend (#3)
  • Android (in progress — early alpha in android/) — a custom keyboard (IME) with on-device whisper.cpp, so dictation works in every app (#6)
  • iOS — keyboard extension, sibling of the Android app (#7)
  • i18n of user-facing strings (currently Polish — the author dictates in Polish) (#5)

Each item has a tracking issue with implementation notes — grab one, comment, and go.

Contributing

PRs and issues welcome. Ground rules are short:

  • uv run pytest must stay green and hardware-free — mock the OS, not the logic.
  • Module boundaries are load-bearing: recorder/transcriber/injector/overlay/ micmute must not import each other. The daemon is the only composer.
  • One platform assumption per module, documented in its docstring.

License

MIT © Filip Piątek

About

Local, GPU-accelerated voice dictation for Linux (GNOME/Wayland). Hotkey → speak → text in any window. Whisper on your own hardware — no cloud, no subscription.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages