Published on July 19, 2026

dictatd 1 - i made my computer listen to me

I type slow. I talk fast. Thoughts run at something like x words per minute, fingers manage more like y. The gap, x minus y, that's friction.

(more of a bottleneck than I like admitting)

Whisper is good. But it gets words wrong sometimes, and the grammar coming out the other end is a mess. So I built the thing I actually wanted. Hold a key, speak, and the text appears already fixed, wherever the cursor happens to be. Gmail, terminal, doesn't matter.

(also no cloud, nothing leaves the machine, that's a part 2 topic)

The pipeline is basically the whole story, so here it is.

Essay imagemic to ring buffer to whisper to llama to paste into field

why a ring buffer and not a file

arecord grabs the mic at 16khz and writes it into a ring buffer that holds the last 20 seconds. Not a file, a fixed block of memory that keeps getting overwritten. One thread writes into it continuously, another thread reads from it. A file would mean an ever-growing recording for a session that might run for hours, and reads waiting on writes to finish. A ring buffer sidesteps both. Fixed size, and old audio just gets overwritten once it's no longer needed. Nothing to clean up, ever.

why the chunks overlap

whisper.cpp reads that buffer in 3 second chunks, and the chunks overlap on purpose. Whisper tends to mangle the first word or two of whatever chunk it's handed, because there's no audio before it to give context. Overlap means every word gets processed twice, once near the tail of one chunk and again near the front of the next, so a bad guess on one pass usually gets caught by the other. Recording keeps running the whole time this is happening, capture and transcribe are separate threads, so nothing gets dropped while whisper is busy decoding.

fixing grammar, not meaning

The raw transcript goes to a tiny model running in llama.cpp. "i is going to shop" comes out as "i am going to the shop". It's told to fix grammar, not rewrite what I said. (why 0.5b and not something bigger, that's a next time question, there's a real tradeoff there)

why clipboard, not synthetic keystrokes

The corrected sentence lands in whatever window is focused. I push it to the clipboard with wl-copy, then synthesize a single ctrl+v through a virtual keyboard. I could've simulated the text character by character instead, but that gets messy fast. Unicode handling differs per app, timing differs per app, a terminal and a browser text field don't behave the same way. One clipboard write and one paste event dodges all of that. Still a whole story on its own, getting a virtual keyboard device the compositor actually trusts.

the detail that makes it feel alive

Whisper works on a sliding window, and a window has no memory of what it transcribed 5 seconds ago. Left alone, that means every chunk reads like it was spoken by a slightly different person. So I feed the last 300 characters or so of already-committed text back into whisper as prompt tokens for the next chunk. It's not new audio, just context. That's the difference between output that reads like one continuous sentence and output that reads like five separate guesses stitched together.

Essay image16khz waveform with a 3s chunk and overlap region highlighted

Win condition was small. Speak a full sentence, pause, watch it show up corrected. That's it.

It works. Okay-ish.

Next time: why 0.5b and not something bigger, and why this stays offline at all.

subscribe to my substack

Get monthly summaries, books read, essays, and link digests delivered to your inbox.

Get updates viasubstackorsubscribe to RSS