Hyprvoice - Voice-Powered Typing for Hyprland / Wayland
Press a toggle key, speak, and get instant text input. Built natively for Wayland/Hyprland - no X11 hacks or workarounds, just clean integration with modern Linux desktops.
Features
- Toggle workflow: Press once to start recording, press again to stop and inject text
- Wayland native: Purpose-built for Wayland compositors - no legacy X11 dependencies or hacky workarounds
- Real-time feedback: Desktop notifications for recording states and transcription status
- Multiple transcription backends: OpenAI Whisper (planned: whisper.cpp for local processing, and more)
- Smart text injection: Clipboard save/restore with direct typing fallback
- Daemon architecture: Lightweight control plane with efficient pipeline management
Status: Beta - core functionality complete and tested, ready for early adopters
Installation
From AUR (Arch Linux)
# Using your preferred AUR helper
yay -S hyprvoice-bin
# or
paru -S hyprvoice-bin
# Enable user service
systemctl --user enable --now hyprvoice.service
Download Binary
- Download from GitHub Releases
- Install:
wget https://github.com/leonardotrapani/hyprvoice/releases/latest/download/hyprvoice-linux-x86_64
mkdir -p ~/.local/bin
mv hyprvoice-linux-x86_64 ~/.local/bin/hyprvoice
chmod +x ~/.local/bin/hyprvoice
# Add to PATH (add to ~/.bashrc or ~/.zshrc)
export PATH="$HOME/.local/bin:$PATH"
Build from Source
git clone https://github.com/leonardotrapani/hyprvoice.git
cd hyprvoice
# Install Go dependencies
go mod download
# Build the binary
go build -o hyprvoice ./cmd/hyprvoice
# Install locally (optional)
sudo cp hyprvoice /usr/local/bin/
# Or install to user directory
mkdir -p ~/.local/bin
cp hyprvoice ~/.local/bin/
export PATH="$HOME/.local/bin:$PATH" # Add to ~/.bashrc or ~/.zshrc
Requirements
- Wayland desktop (Hyprland, Niri, GNOME, KDE, etc.)
- PipeWire audio system with tools
- System packages:
# Arch Linux
sudo pacman -S pipewire pipewire-pulse pw-record wl-clipboard
# Ubuntu/Debian
sudo apt install pipewire-pulse pipewire-bin wl-clipboard
# Fedora
sudo dnf install pipewire-utils wl-clipboard
For text injection:
# Arch Linux
sudo pacman -S wtype
# Ubuntu/Debian
sudo apt install wtype
# Alternative: ydotool (if wtype unavailable)
# Follow ydotool setup for user permissions
Optional:
notify-send(desktop notifications)systemd --user(daemon service)
Quick Start
- Setup daemon service:
# Enable and start the user service (reccomended)
systemctl --user enable --now hyprvoice.service
# Or run manually in background
hyprvoice serve &
- Configure Hyprland keybind:
# Add to ~/.config/hypr/hyprland.conf
bind = SUPER, R, exec, hyprvoice toggle
- Test voice input:
# Check daemon status
hyprvoice status
# Toggle recording (or use Super+R)
hyprvoice toggle
# Speak something...
hyprvoice toggle # Stop and transcribe
Quick Reference
Common Commands
# Start the daemon
hyprvoice serve
# Toggle recording on/off
hyprvoice toggle
# Check current status
hyprvoice status
# Get protocol version
hyprvoice version
# Stop the daemon (if not using systemd service)
hyprvoice stop
Keybinding Pattern
Most setups use this toggle pattern in window manager config:
bind = SUPER, R, exec, hyprvoice toggle
Keyboard Shortcuts Setup
Hyprland
Add to your ~/.config/hypr/hyprland.conf:
# Hyprvoice - Voice to Text (toggle recording)
bind = SUPER, R, exec, hyprvoice toggle
# Optional: Status check
bind = SUPER SHIFT, R, exec, hyprvoice status && notify-send "Hyprvoice" "$(hyprvoice status)"
Usage Examples
Basic Toggle Workflow
- Press keybind → Recording starts (notification appears)
- Speak your text → Audio captured in real-time
- Press keybind again → Recording stops, transcription begins
- Text appears → Injected at cursor position or clipboard
CLI Usage
# Start daemon manually (if not using systemd service)
hyprvoice serve
# In another terminal: toggle recording
hyprvoice toggle
# ... speak ...
hyprvoice toggle
# Check what's happening
hyprvoice status
Configuration
Configuration is automatically loaded from ~/.config/hyprvoice/config.toml. The daemon creates this file with sensible defaults and helpful comments on first run. Changes to the config file are applied immediately without restarting the daemon.
Transcription Providers
Hyprvoice supports multiple transcription backends:
Generated Configuration Example
The daemon automatically creates ~/.config/hyprvoice/config.toml with helpful comments:
# Hyprvoice Configuration
# This file is automatically generated with defaults.
# Edit values as needed - changes are applied immediately without daemon restart.
# Audio Recording Configuration
[recording]
sample_rate = 16000 # Audio sample rate in Hz (16000 recommended for speech)
channels = 1 # Number of audio channels (1 = mono, 2 = stereo)
format = "s16" # Audio format (s16 = 16-bit signed integers)
buffer_size = 8192 # Internal buffer size in bytes (larger = less CPU, more latency)
device = "" # PipeWire audio device (empty = use default microphone)
channel_buffer_size = 30 # Audio frame buffer size (frames to buffer)
timeout = "5m" # Maximum recording duration (e.g., "30s", "2m", "5m")
# Speech Transcription Configuration
[transcription]
provider = "openai" # Transcription service ("openai" only currently supported)
api_key = "" # OpenAI API key (or set OPENAI_API_KEY environment variable)
language = "" # Language code (empty for auto-detect, "en", "it", "es", "fr", etc.)
model = "whisper-1" # OpenAI model name ("whisper-1" recommended)
# Text Injection Configuration
[injection]
mode = "fallback" # Injection method ("clipboard", "type", "fallback")
restore_clipboard = true # Restore original clipboard after injection
wtype_timeout = "5s" # Timeout for direct typing via wtype
clipboard_timeout = "3s" # Timeout for clipboard operations
# Desktop Notification Configuration
[notifications]
enabled = true # Enable desktop notifications
type = "desktop" # Notification type ("desktop", "log", "none") -- always keep "desktop" unless debugging
whisper.cpp Local (Planned) -> Not yet implemented
Private, offline transcription using local models:
[transcription]
provider = "whisper_cpp"
model_path = "~/models/ggml-base.en.bin"
threads = 4
Recording Configuration
Audio capture settings:
[recording]
sample_rate = 16000 # Audio sample rate in Hz
channels = 1 # Number of audio channels (1 for mono)
format = "s16" # Audio format (s16 recommended)
buffer_size = 8192 # Internal buffer size in bytes
device = "" # PipeWire device (empty for default)
channel_buffer_size = 30 # Audio frame buffer size
timeout = "5m" # Maximum recording duration (prevents runaway recordings)
Recording Timeout:
- Prevents accidental long recordings that could consume resources
- Default: 5 minutes (
"5m") - Format: Go duration strings like
"30s","2m","10m" - Recording automatically stops when timeout is reached
Text Injection
Configurable text injection with multiple modes:
[injection]
mode = "fallback" # "clipboard", "type", or "fallback"
restore_clipboard = true
wtype_timeout = "5s"
clipboard_timeout = "3s"
Injection Modes:
fallback(default): Try direct typing first, fallback to clipboardtype: Direct typing using wtype onlyclipboard: Copy to clipboard only
Behavior:
restore_clipboard = true: Save and restore original clipboard content- Smart fallback ensures text injection always succeeds when possible
Notifications
Desktop notification settings:
[notifications]
enabled = true # Enable/disable notifications
type = "desktop" # "desktop", "log", or "none"
Notification Types:
desktop: Use notify-send for desktop notificationslog: Log messages to console onlynone: Disable all notifications
Always keep type = "desktop" unless debugging.
Configuration Hot-Reloading
The daemon automatically watches the config file for changes and applies them immediately:
- Notification settings: Applied instantly
- Injection settings: Applied to current and future operations
- Recording/Transcription settings: Applied to new recording sessions
- Invalid configs: Rejected with error notification, daemon continues with previous config
Service Configuration
Systemd Service
The daemon runs as a user service:
# Create service file
mkdir -p ~/.config/systemd/user
cat > ~/.config/systemd/user/hyprvoice.service << 'EOF'
[Unit]
Description=Hyprvoice voice-to-text daemon
After=pipewire.service
[Service]
Type=simple
ExecStart=/usr/local/bin/hyprvoice serve
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.target
EOF
# Enable and start
systemctl --user daemon-reload
systemctl --user enable --now hyprvoice.service
File Locations
- Socket:
~/.cache/hyprvoice/control.sock- IPC communication - PID file:
~/.cache/hyprvoice/hyprvoice.pid- Process tracking - Config:
~/.config/hyprvoice/config.toml- User settings (planned)
Development Status
| Component | Status | Notes |
|---|---|---|
| Core daemon & IPC | ✅ | Unix socket control plane |
| Recording workflow | ✅ | Toggle recording via PipeWire |
| Audio capture | ✅ | Efficient PipeWire integration |
| Desktop notifications | ✅ | Status feedback via notify-send |
| OpenAI transcription | ✅ | HTTP API integration |
| Text injection | ✅ | Clipboard + wtype with fallback |
| Configuration system | ✅ | TOML-based user settings with hot-reload |
| Unit test coverage | ✅ | Comprehensive test suite (100% pass) |
| Installation (AUR etc) | ⏳ | Installation via AUR and easy setup |
| Light dictation models | ⏳ | Alternatives to whispers for light and fast dictation |
| whisper.cpp support | ⏳ | Local model inference |
Legend: ✅ Complete · ⏳ Planned
Architecture Overview
Hyprvoice uses a daemon + pipeline architecture for efficient resource management:
- Control Daemon: Lightweight IPC server managing lifecycle
- Pipeline: Stateful audio processing (recording → transcribing → injecting)
- State Machine:
idle → recording → transcribing → injecting → idle
System Architecture
flowchart LR
subgraph Client
CLI["CLI/Tool"]
end
subgraph Daemon
D["Control Daemon (lifecycle + IPC)"]
end
subgraph Pipeline
A["Audio Capture"]
T["Transcribing"]
I["Injecting (wtype + clipboard)"]
end
N["notify-send/log"]
CLI -- unix socket --> D
D -- start/stop --> A
A -- frames --> T
T -- status --> D
D -- events --> N
D -- inject action --> T
T --> I
I -->|done| D
stateDiagram-v2
[*] --> idle
idle --> recording: toggle
recording --> transcribing: first_frame
transcribing --> injecting: inject_action
injecting --> idle: done
recording --> idle: abort
injecting --> idle: abort
How It Works
- Toggle recording → Pipeline starts, audio capture begins
- Audio streaming → PipeWire frames buffered for transcription
- Toggle stop → Recording ends, transcription starts
- Text injection → Result typed or copied to clipboard
- Return to idle → Pipeline cleaned up, ready for next session
Data Flow
toggle(daemon) → create pipeline → recording- First frame arrives → transcribing (daemon may notify
Transcribinglater) - Audio frames → audio buffer (collect all audio during session)
- Second
toggleduring transcribing → sendinjectaction → transcribe collected audio → injecting (simulated) - Complete → idle; pipeline stops; daemon clears reference
- Notifications at key transitions
Troubleshooting
Common Issues
Daemon Issues
Daemon won't start:
# Check if already running
hyprvoice status
# Check for stale files
ls -la ~/.cache/hyprvoice/
# Clean up and restart
rm -f ~/.cache/hyprvoice/hyprvoice.pid
rm -f ~/.cache/hyprvoice/control.sock
hyprvoice serve
Command not found:
# Check installation
which hyprvoice
# Add to PATH if using ~/.local/bin
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
Audio Issues
No audio recording:
# Check PipeWire is running
systemctl --user status pipewire
# Test microphone
pw-record --help
pw-record test.wav
# Check microphone permissions and levels
Audio device issues:
# List available audio devices
pw-cli list-objects | grep -A5 -B5 Audio
# Check microphone is not muted in system settings
Notification Issues
No desktop notifications:
# Test notify-send directly
notify-send "Test" "This is a test notification"
# Install if missing
sudo pacman -S libnotify # Arch
sudo apt install libnotify-bin # Ubuntu/Debian
Text Injection Issues
Text not appearing:
-
Ensure cursor is in a text field when toggling off recording
-
Check that
wtypeandwl-clipboardtools are installed:# Test wtype directly wtype "test text" # Test clipboard tools echo "test" | wl-copy wl-paste -
Verify Wayland compositor supports text input protocols
-
Check injection mode in configuration (fallback mode is most robust)
Clipboard issues:
# Install wl-clipboard if missing
sudo pacman -S wl-clipboard # Arch
sudo apt install wl-clipboard # Ubuntu/Debian
# Test clipboard functionality
wl-copy "test text"
wl-paste
Debug Mode
# Run daemon with verbose output
hyprvoice serve
# Check logs from systemd service (or just see results from hyprvoice serve)
journalctl --user -u hyprvoice.service -f
# Test individual commands
hyprvoice toggle
hyprvoice status
Development
Building from Source
git clone https://github.com/leonardotrapani/hyprvoice.git
cd hyprvoice
# Install Go dependencies
go mod download
# Build
CGO_ENABLED=1 go build -o hyprvoice ./cmd/hyprvoice
# Run tests
go test ./...
# Install locally
sudo cp hyprvoice /usr/local/bin/
Project Structure
hyprvoice/
├── cmd/hyprvoice/ # CLI application entry point
├── internal/
│ ├── bus/ # IPC (Unix socket) + PID management
│ ├── daemon/ # Control daemon (lifecycle management)
│ ├── injection/ # Text injection (clipboard + wtype)
│ ├── notify/ # Desktop notification integration
│ ├── pipeline/ # Audio processing pipeline + state machine
│ ├── recording/ # PipeWire audio capture
│ └── transcriber/ # Transcription adapters (OpenAI, whisper.cpp)
├── go.mod # Go module definition
└── README.md
Development Workflow
# Terminal 1: Run daemon with logs
go run ./cmd/hyprvoice serve
# Terminal 2: Test commands
go run ./cmd/hyprvoice toggle
go run ./cmd/hyprvoice status
go run ./cmd/hyprvoice stop
IPC Protocol
Simple single-character commands over Unix socket:
t- Toggle recording on/offs- Get current statusv- Get protocol versionq- Quit daemon gracefully
Contributing
Contributions welcome! Please:
- Follow existing code conventions and patterns
- Add tests for new functionality when available
- Update documentation for user-facing changes
- Test on Hyprland/Wayland before submitting PRs
License
MIT License - see LICENSE.md for details.