All articles
Open Source / AlgoFly Journal

Voice Keyboard: Self-Hosted Whisper Dictation That Types into Your Apps

Turn speech into keyboard input with your own Whisper server, desktop clients, and an optional ESP32 Bluetooth keyboard.

Voice Keyboard — self-hosted dictation | AlgoFly Journal
In this article

Dictation becomes much more useful when the words land where you are already working. A browser transcript is helpful, but copying it into an editor, chat window, or document after every recording interrupts the flow.

Voice Keyboard connects speech recognition to a selected keyboard client. Record in a browser, transcribe on your own server with Whisper, and send the resulting text to the focused window on your computer. A second client option uses an ESP32 board as a Bluetooth keyboard, allowing a paired device to receive text without installing a desktop application.

How the speech-to-keyboard pipeline works

The system separates three jobs: capturing audio, transcribing it, and typing the result. Your browser handles the microphone interface. The server runs the speech model. A registered desktop client or ESP32 board receives text and produces keyboard input.

Browser microphone
        ↓
HTTPS gateway → Whisper transcription on your server
        ↓
Selected client connection
        ↓
Focused application on the target device

Only the active client for an account receives dictation. This makes it possible to keep clients on several machines while choosing which one should type. The separation also means a phone can be the microphone while a workstation is the destination.

Self-hosting places transcription under your control. The browser still sends audio to the server you configure; if you reach that server through a public proxy or tunnel, that route is part of your deployment. Model files must also be downloaded on the first start.

Running the server with Docker

The documented setup requires Docker with Compose v2. An NVIDIA GPU can accelerate transcription when the NVIDIA Container Toolkit is available. CPU operation is supported, with slower processing.

git clone https://github.com/algofly-oss/voice-keyboard.git
cd voice-keyboard
./setup.sh

The setup script creates the environment file if needed, generates initial credentials, chooses a CPU or GPU profile, discovers local addresses, and selects available HTTP and HTTPS ports. It then builds and starts the stack and prints the connection details.

On the first start, allow time for the Whisper model download. A running container does not necessarily mean the model has finished loading. Check the logs if the interface is reachable before transcription is ready.

docker compose ps
docker compose logs -f transcription
# For the CPU profile, inspect transcription-cpu instead.

The manual path starts with .env.example. Configure at least the initial account password, session secret, server names, and Compose profile, then run docker compose up -d --build. Keep the session secret stable across ordinary redeployments.

HTTPS is part of the microphone experience

Browsers require a secure context for microphone access. An HTTP interface that looks usable may still be unable to record audio from a remote device. Voice Keyboard includes Caddy to provide HTTPS and manage certificates.

For a local network installation, Caddy uses a local certificate authority. Trust that authority on each browser device using the instructions printed by setup. For a public domain reachable on the required ports, the documented configuration can use Let’s Encrypt instead.

Set VK_DOMAIN to every hostname or IP address used to reach the server. The certificate configuration must cover the address a device actually opens. If you terminate HTTPS at an existing reverse proxy or tunnel, choose the appropriate VK_PROTOCOL setting rather than creating conflicting redirects.

The HTTPS guide covers LAN access, public domains, Cloudflare Tunnel, port forwarding, and existing reverse proxies. Follow the scenario matching your installation.

Choosing transcription or translation

The normal transcription variant uses Whisper large-v3-turbo by default. It aims to type what you say in the selected language. The translation variant uses large-v3 to produce English text from speech in other languages.

# In .env: ordinary speech-to-text
WHISPER_VARIANT=transcribe
WHISPER_LANGUAGE=en

# Or choose English translation of speech:
# WHISPER_VARIANT=translate

The interface offers language selection, including a Hinglish mode that renders Hindi in Latin letters. The README also documents transliteration behavior when English is selected and Whisper produces words in an Indian script.

Settings such as compute type, beam size, and transcription concurrency affect resource use and behavior. Start with the defaults, then tune against your own recordings. More concurrency is not automatically better on a GPU with limited memory. The default concurrency is one, so simultaneous requests queue for the shared model.

Installing a desktop client

Open Settings → Clients in the web interface and choose the destination operating system. The interface supplies a server-specific installation command with a pairing token. Use that generated command rather than a token copied from an example.

The desktop program, vkeyboard, runs in the background and reconnects after interruptions. It supports macOS, Windows, and Linux on x86-64 and ARM64. The implementation is a self-contained Go binary, so the destination computer does not need a Python or .NET runtime for the client.

vkeyboard status
vkeyboard logs
vkeyboard type-test
vkeyboard stop
vkeyboard start

After installation, select the client in the browser before dictating. The focused application receives the input, so put the cursor in a scratch document for the first test. This is a useful way to verify the entire path from microphone permission to typing permissions.

Platform-specific typing behavior

PlatformInput methodImportant detail
macOSCoreGraphics Unicode eventsGrant Accessibility permission; an upgrade may prompt again
WindowsSendInput Unicode eventsTyping into elevated applications requires matching elevation
Linux X11XTestSupports Unicode input through the documented client path
Linux WaylandVirtual input deviceRequires uinput access; the documented method is US-layout ASCII

The client exposes typing speed and method settings. On macOS and Windows, a paste mode can use the clipboard for longer text while restoring supported previous clipboard content. Linux uses typing rather than assuming a universal paste shortcut.

Using an ESP32 as a Bluetooth keyboard

The hardware option is especially useful when you want the destination device to see a normal Bluetooth keyboard. The ESP32 joins Wi-Fi, connects to the server, receives dictated text, and emits Bluetooth HID input to its paired host.

In Chrome or Edge, open Settings → Clients → Add an ESP32 board. Connect an ESP32 or ESP32-C3 over USB, then use the browser’s flashing workflow to install firmware and configure Wi-Fi, the server, and the device name. Pair that name in the destination device’s Bluetooth settings.

The board can be updated and reconfigured from the same page. Its typing capability is narrower than the Unicode desktop clients: it follows a US keyboard layout, simplifies accented Latin letters, and skips unsupported scripts. Choose the client type based on the language and destination application you need.

Moving between home and remote access

A server may be reachable directly over your LAN and through a public hostname when you are away. Configure VK_URLS with those complete addresses, and ensure their names also appear in VK_DOMAIN.

VK_DOMAIN=192.168.1.20,voice.example.com
VK_URLS=https://192.168.1.20:8272,https://voice.example.com

Clients learn the advertised addresses and prefer a reachable local address before falling back to the public one. They verify that an alternative belongs to the same server before switching. The browser’s optional fastest-address behavior waits until the page is idle before moving to a LAN address.

This improves convenience across network changes without treating every reachable server as interchangeable. Browsers still need to trust the local certificate, and sign-in is per address.

Accounts, updates, and useful operational checks

Account creation is disabled by default. Each account has its own clients and dictation, while all accounts share the transcription model. The first account is created from the initial environment configuration; subsequent password changes happen in the interface.

Back up backend/volumes, including account data and the certificate authority trusted by your devices. Remove a lost client through Settings → Clients, and rotate an installation command if its pairing token is exposed. Client releases can be mirrored from the configured GitHub repository and distributed by your server.

For troubleshooting, check the pipeline in order: trusted HTTPS, browser microphone permission, model readiness, an active client connection, and operating-system typing permission. This narrows the problem far more effectively than repeatedly reinstalling everything.

Build a dictation workflow around your own devices

Voice Keyboard connects a local speech model to a practical destination: the application you are already using. A phone, a GPU server, a desktop agent, and an optional Bluetooth board can each handle the part of the workflow they are best suited for.

Technical source snapshot: 763cc44. Actual latency and recognition quality depend on your hardware, language, microphone, model, and network; no new performance benchmark is claimed here.

WRITTEN BY

AlgoFly Team

Engineering ideas, practical guides, and the tools we build at AlgoFly.

Explore more articles →