Press a hotkey, speak, and paste the transcription at your cursor. ok-voice
is a toggle-driven voice-to-text application for Linux on X11 and Wayland
using a configurable OpenAI-compatible Whisper endpoint.
- Toggle mode: first press starts recording, second press stops
- Batch transcription: uploads one completed WAV through the standard
/v1/audio/transcriptionsAPI; no Realtime API or WebSocket - Automatic language detection: no language configuration required
- Unicode output: transfers UTF-8 through the desktop clipboard
- Babashka
parecordfrom PulseAudio utilitiesnotify-sendfrom libnotify- On X11:
xdotool,xclip, andxbindkeysor another hotkey manager - On Wayland:
wl-clipboard,ydotool, a runningydotoold, and a desktop hotkey binding - An OpenAI-compatible transcription endpoint and Bearer credential
On Debian/Ubuntu with X11:
sudo apt install pulseaudio-utils xdotool xclip libnotify-bin xbindkeysOn Debian/Ubuntu with Wayland:
sudo apt install pulseaudio-utils wl-clipboard ydotool libnotify-binThe user running ok-voice must be able to access the ydotoold socket.
ydotool honors YDOTOOL_SOCKET when set and otherwise uses its standard
socket under $XDG_RUNTIME_DIR, with /tmp as a fallback.
Create the configuration directory and copy the example:
mkdir -p ~/.config/ok-voice
cp resources/config.example.yaml ~/.config/ok-voice/config.yaml
chmod 600 ~/.config/ok-voice/config.yamlEdit ~/.config/ok-voice/config.yaml:
api-url: "https://whisper-turbo.twohundredok.com/v1/audio/transcriptions"
api-key: "your-api-key-here"
model: "large-v3-turbo"
allow-insecure-http: falseapi-url must be the complete transcription endpoint, including
/v1/audio/transcriptions. The request uses the OpenAI multipart fields
file, model, and response_format=json, with Bearer authentication.
The following environment variables are used when the corresponding config value is absent:
| Config | Environment | Default |
|---|---|---|
api-url |
OPENAI_API_URL |
https://api.openai.com/v1/audio/transcriptions |
api-key |
OPENAI_API_KEY |
none |
model |
OPENAI_MODEL |
whisper-1 |
Remote plain-HTTP endpoints are rejected because they expose the credential
and recorded audio. HTTPS is recommended. Localhost HTTP is allowed; another
HTTP endpoint requires allow-insecure-http: true.
Never commit the real user configuration or an API key. The tracked
resources/config.example.yaml contains placeholders only.
Add a binding to ~/.xbindkeysrc:
"cd ~/src/200ok/ok-voice/ && bb -m ok-voice.core"
F9
Change the checkout path and key as needed, then reload xbindkeys:
xbindkeys --poll-rcRun the application once to start recording:
bb -m ok-voice.coreRun the same command again to stop and transcribe. The Babashka task is also
available as bb ok-voice. Notifications indicate recording, transcription,
completion, and error states.
- The first invocation records 16 kHz mono audio from the default PulseAudio input into a temporary WAV file.
- The second invocation writes a runtime stop request.
- The recording process uploads the WAV to the configured
OpenAI-compatible endpoint and reads the JSON
textresponse. - The focused window receives the transcript through the UTF-8 clipboard.
- Temporary WAV and runtime state files are removed.
The transcript is transferred through the desktop clipboard rather than typed as synthetic character keypresses. Character key events cannot represent arbitrary Unicode reliably and can corrupt characters such as German umlauts.
ok-voice restores the previous textual clipboard after pasting. On X11, it
uses Shift+Insert for normal applications and Ctrl+Shift+V for Kitty,
selected from the target window's X11 class; xdotool restores window focus
and sends the shortcut. On Wayland, wl-clipboard negotiates an available text
MIME type and ydotool sends Shift+Insert to the focused window.
clj-kondo --lint src --fail-level warning
git diff --checkThis project is 100% vibe coded with
GSD. The full planning artifacts
are in the .planning/ folder.
AGPL-3.0 - see LICENSE for details.