Important
This project is for Linux only (X11 and Wayland).
Based on stt-mcp-server-linux by marcindulak, licensed under Apache 2.0.
Local speech-to-text for Linux using Whisper.
The system uses a split architecture:
- Container: Runs Whisper transcription model, exposes HTTP API
- Host: Lightweight Python client handles keyboard, audio, and text injection via ydotool
Text injection works in any application (terminals, GUI apps, browsers) on both X11 and Wayland.
Warning
This project will create ~/.speech-to-text directory.
Host
+-------------+ audio +-------------+ text +-------+
| evdev + | ----------> | Container | ---------> | ydo |
| sounddevice | HTTP POST | /transcribe | | tool |
+-------------+ +-------------+ +-------+
Benefits:
- Container needs zero device access (secure, simple Docker setup)
- Host component is lightweight (~300 lines Python)
- Transcription API reusable for other purposes
- Can swap transcription backend without touching host code
- Container can run on different machine (remote transcription)
- ydotool works in all applications
- Works on both X11 and Wayland
-
Install Docker Engine or Docker Desktop
-
Install ydotool:
# Arch/Manjaro sudo pacman -S ydotool # Debian/Ubuntu sudo apt install ydotool
-
Configure uinput access for ydotool:
# Create udev rule echo 'KERNEL=="uinput", GROUP="input", MODE="0660"' | sudo tee /etc/udev/rules.d/99-uinput.rules # Add user to input group sudo usermod -aG input $USER # Reload rules sudo udevadm control --reload-rules sudo udevadm trigger
Log out and back in for group changes to take effect.
-
Install Python dependencies for the host client:
pip install evdev sounddevice requests
-
Clone this repository:
git clone https://github.com/literal/speech-to-text cd speech-to-text -
Build the Docker image:
bash scripts/build_docker_image.sh
-
Download the Whisper model:
bash scripts/download_whisper_model.sh
Install and start the systemd services:
sudo bash scripts/setup_services.shThis creates and enables three services:
ydotoold- keyboard/mouse input daemon (system service)stt-server- transcription server in Docker (system service)stt-client- keyboard monitor and audio client (user service)
Note
The stt-client runs as a user service to access PipeWire audio.
Manage system services:
sudo systemctl status stt-server ydotoold
sudo systemctl restart stt-server
sudo journalctl -u stt-server -fManage stt-client (user service):
systemctl --user status stt-client
systemctl --user restart stt-client
journalctl --user -u stt-client -fPress and hold Right Super (Right Windows key) to record. Release to transcribe and inject text at the cursor. The activation key can be changed in the configuration.
Configuration options are set in scripts/setup_services.sh. Edit this file before running the setup, or modify the service files afterwards.
| Variable | Default | Description |
|---|---|---|
MODEL |
small.en |
Whisper model to use |
PORT |
5000 |
HTTP port for the transcription API |
Service file: /etc/systemd/system/stt-server.service
| Variable | Default | Description |
|---|---|---|
KEYBOARD_LAYOUT |
de |
Keyboard layout (us, de) |
LANGUAGE |
auto |
Transcription language or auto |
KEY |
KEY_RIGHTMETA |
Activation key |
PAD_SECONDS |
30 |
Silence padding in seconds |
Service file: ~/.config/systemd/user/stt-client.service
After modifying service files, reload and restart:
# For stt-server
sudo systemctl daemon-reload
sudo systemctl restart stt-server
# For stt-client
systemctl --user daemon-reload
systemctl --user restart stt-clientbash scripts/test_unit.shbash scripts/test_mypy.shFlask HTTP API that:
- Loads Whisper model at startup
- Exposes
/transcribeendpoint (POST raw PCM audio) - Exposes
/healthendpoint for container health checks - Exposes
/infoendpoint (returns model name and load status) - Returns JSON with transcribed text
Lightweight Python client that:
- Monitors keyboard for activation key (evdev)
- Records audio while key is held (sounddevice)
- Sends audio to container API (requests)
- Injects text via ydotool (subprocess)