Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

dictate

Personal push-to-talk dictation for Windows. Hold a key, speak, release — the text appears wherever you were typing.

No window, no browser engine, no settings screen. Configuration is one TOML file; feedback is a tray icon and a small bubble that reacts to your voice. That is the whole reason it is around a thousand lines rather than tens of thousands.

Built for myself. Published because it might be a useful starting point for someone else.

What it does

hold hotkey  →  record mic  →  release  →  transcribe locally  →  type into the focused app
  • Fully local. Speech recognition runs on your machine. Nothing is uploaded.
  • Reacts to your voice. The bubble's bars follow your actual microphone level, so silence is flat and speech moves them.
  • Stays out of the way. The bubble never takes focus, so the caret stays in the app you are dictating into.
  • Correct clipboard handling. Text delivery goes through win-text-inject, which restores your previous clipboard only after the target has read the new text, and keeps transcripts out of Windows clipboard history.

Architecture

Five files, each one job.

main.rs       the loop, and the resident model
audio.rs      microphone capture, downmix, level metering, resample to 16 kHz
config.rs     dictate.toml
autostart.rs  the HKCU Run key
ui.rs         tray icon and the voice-reactive bubble

Three threads, so the parts that must not block each other never do: the keyboard hook, the audio callback, and the UI message pump each run on their own, and the main loop coordinates them.

thread architecture and dataflow

Almost everything is thin glue over a crate. ui.rs is the largest file because the tray icon and the bubble are raw Win32 GDI, which nothing does for you, and because the bubble has one hard requirement — it must never steal focus, or the text lands nowhere.

Job Crate Notes
Global hold-to-talk hotkey (incl. modifier-only bindings) handy-keys reports press and release, which RegisterHotKey cannot
Microphone capture cpal its callback also feeds the bubble's level meter
Resampling to 16 kHz rubato anti-aliased, not naive decimation
Speech recognition transcribe-cpp ggml; loads the same GGUF models Handy uses
Text delivery win-text-inject built for exactly this; see below

Why the text-delivery step is its own crate

Pasting a transcript into whatever app has focus is the step every dictation tool gets subtly wrong, so it lives in a separate, tested crate. Everything it fixes is something this app hits by construction:

text delivery decision flow

  • The hotkey modifier is held by construction when this app pastes — it is a push-to-talk key you are still pressing. Sanitizing it is not an edge case here, it is every single press.
  • Because delivery is a paste, your clipboard would be destroyed on every dictation without the delayed-render restore. That is Handy issue #502, and the fix is the whole reason this app does not have it.
  • Every sentence you speak passes through the clipboard, so without the four privacy opt-out formats it would land in Windows clipboard history and cloud sync — quietly breaking the "fully local" promise.

Install and use

You need two things in one folder: dictate.exe and a speech model. That is the whole install. The default build links the recognition engine into the executable, so there are no DLLs to ship and no runtime to install.

1. Get dictate.exe

Either grab a release binary, or build it (see Building below). Put it in a folder you will keep, for example C:\Users\you\Apps\dictate\. Do not run it from a Downloads or temp folder if you plan to enable autostart, since those get cleaned.

2. Get a model

dictate uses GGUF speech models — the same ones Handy uses. Download one .gguf file from huggingface.co/handy-computer and put it next to the exe. Good starting points:

Model Size Notes
canary-180m-flash ~200 MB tiny and instant; English, German, Spanish, French
parakeet-tdt-0.6b-v3 ~740 MB more accurate; 25 European languages

Download the Q8_0 file (best size/quality balance). Nothing downloads automatically — the model is yours, placed by you, and never fetched behind your back.

3. First run

Run dictate.exe once. It writes a commented dictate.toml next to itself and stops. Open that file, set model to your .gguf path, and run again:

hotkey = "CtrlLeft+WinLeft"
model  = "C:/Users/you/Apps/dictate/canary-180m-flash-Q8_0.gguf"
language = "en"

# Optional. Keeps the first word from being clipped by buffering recent audio,
# at the cost of holding the mic open (the Windows indicator stays lit). 0 = off.
preroll_ms = 0

# Optional. Literal, case-insensitive, whole-word fixes for names and jargon.
replacements = [["kubernetes", "Kubernetes"], ["github", "GitHub"]]

Forward slashes or escaped backslashes both work in the path.

Leading and trailing silence is trimmed from every recording automatically, which improves accuracy and cuts latency; there is no setting for it.

4. Dictate

Hold the hotkey, speak, release. A small bubble appears bottom-center and its bars follow your voice. The text lands wherever your cursor was. Right-click the tray icon to open the config or exit.

Start on login

Set autostart = true in dictate.toml and restart it once. It writes an entry under HKCU\...\Run pointing at wherever the exe currently lives, and re-asserts it on every launch. Set it back to false and restart to remove the entry. This is why the exe should live in a permanent folder before you enable it.

Building

Needs the Rust MSVC toolchain, plus CMake and the MSVC C++ build tools, because transcribe-cpp compiles a ggml engine from source.

cargo build --release

The result is a self-contained CPU build: target/release/dictate.exe, no DLLs. Canary 180M runs comfortably in real time on a modern CPU, so the GPU is not needed.

Optional GPU acceleration. For a larger model on a machine with a GPU, build with the Vulkan backend:

cargo build --release --features vulkan-gpu

That produces loadable ggml backend DLLs; they must sit next to the exe, and building them also needs the Vulkan SDK installed. For most people the CPU build is simpler and fast enough.

Configuration

On first run dictate writes a commented dictate.toml next to the executable and stops so you can fill it in.

# Hold this to record. Modifier-only bindings work and are usually the most
# comfortable, because they cannot collide with an application shortcut.
#   "CtrlLeft+WinLeft"   "CtrlRight"   "AltRight"   "Ctrl+Space"   "F13"
hotkey = "CtrlLeft+WinLeft"

# Absolute path to a GGUF speech model. Any model transcribe-cpp supports works;
# Parakeet and Canary are good CPU choices. Handy's models live under
# ~/.cache/huggingface/hub if you already have it installed.
model = "C:/path/to/model.gguf"

# Language hint. Comment out to let the model detect, which is less accurate on
# short utterances.
language = "en"

# Ignore recordings shorter than this, so an accidental tap does not produce a
# hallucinated transcript.
min_recording_ms = 400

# Keep the model resident between dictations. Costs idle RAM, saves seconds per
# dictation, since loading dominates everything else.
keep_model_loaded = true

# Start with Windows. Toggling this and restarting is enough in either direction.
autostart = false

The tray icon's right-click menu opens this file and exits the app.

Deliberately not here

This is a personal tool, and the absences are the point.

  • No model manager or download UI — put a .gguf path in the config.
  • No settings window — it is a text file.
  • No cross-platform support — Windows only.
  • No auto-update, no telemetry, no account.

Status

Works, and used daily by its author. Rough edges remain: the bubble's shape and animation are tuned by hand-editing constants, there is no voice-activity trimming, and the paste-chord table is small. Contributions and forks welcome, but it is shaped for one person's use first.

Related

Text delivery goes through win-text-inject, published separately because the clipboard-restore problem it solves is not specific to dictation.

License

MIT OR Apache-2.0.

About

personal push-to-talk dictation for windows. hold a key, speak, release, and the text appears where you were typing. no window, no webview, one toml file.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages