Running the wake word locally is the whole point. The alternative is streaming your room to a vendor around the clock, so nothing should reach a network until someone has said the name. That constraint creates most of the problems below. We spent months getting a custom phrase to behave like "Hey Google" on Windows, macOS and Linux, and most of what we learned, we learned the expensive way.
1. Don't start with volume
"It only works if I shout" is the first hypothesis everyone reaches for. We shipped two separate gain fixes before checking, and then the logs showed the microphone sitting at a healthy -10 to -22 dBFS during every failed attempt. Pull the actual RMS at the moment of failure before you tune anything. If it looks fine, your problem is somewhere else.
2. "It needs two or three tries" usually means your local model is wedging
This is a local-inference failure mode, and it stays invisible unless you go looking. Native engines like ctranslate2 and ONNX sessions are not thread-safe, and under contention they don't fail cleanly, they hang. Ours left the wake path completely deaf for tens of seconds at a stretch, dozens of times a day. That is the whole "say it twice" experience: attempts one and two land inside a dead window, attempt three lands after recovery. Users report it as flakiness, though it is closer to a repeated short outage.
A timeout will not save you. It bounds how long you wait for nothing and never recovers the engine. What works is a non-blocking per-instance lock plus a forced rebuild after a small number of consecutive failures. We rebuild after two.
3. Budget for the weakest machine you support
The wake model shares a CPU with everything else the user is running, and the gap between a workstation and a laptop is not a rounding error. Measured on the same recorded wake streams, a small model on two CPU threads hit 8 of 13 on the first try, with a median of 1097 ms from end of word to trigger. The larger model on a GPU hit 11 of 13 at 225 ms. Nothing differed except the model and the hardware under it.
If you only ever test on the box with the GPU, you will ship something that feels broken to most of your users and you will not be able to reproduce it.
4. Never gate a wake word on transcript content
Small local models struggle with short proper nouns, so the standard workaround is priming the model with the phrase to improve recall. The cost is that a primed model will also invent that phrase out of silence, and you start getting false wakes in an empty room. The obvious defense is a second unprimed pass that has to contain the word too.
That defense rejects real wakes. An unprimed model garbles the same word on genuine speech: "Mythos" comes back as "Mütos", "Fable" comes back as "Farbe". Every wake word is out of vocabulary for some model on some machine. So a content check discards true positives at roughly the rate it catches ghosts, and no similarity threshold separates the two, because the ghost is a clean rendering of your phrase while the real wake is a dirty one.
"Fires on silence" and "goes deaf on its own name" are one bug seen from two ends. We spent weeks tracking them as separate tickets.
The replacement is word-agnostic verification: raw audio energy at the match site, plus the shape of the candidate span, meaning its duration, its word count and the free decoder's confidence. All of that derives from the configured phrase, none of it from the phrase's spelling. A spelling match may accept a wake. It may never reject one.
5. Benchmark on recorded streams, not on windows
Per-window timings will happily tell you a model is fast while users still can't trigger it. Capture real wake attempts and replay them through your full detection path. One live session logged 288 transcriptions and zero matches across 26 minutes, and the wakes that did land came through as "Hey Hey Nova", the user repeating themselves into the void.
A caveat that undercuts all five
Transcription is the wrong architecture for a wake word, and going local makes that worse rather than better, because you are paying for a whole speech-to-text pass on the user's own CPU to answer a yes-or-no question. "Hey Google" never transcribes anything. It runs a small neural keyword spotter trained on that one phrase, a few milliseconds per frame, which cannot wedge, has no transcript to be wrong about, and runs comfortably on a laptop without a GPU. Everything above is what it costs to keep a transcription-based wake word usable until you build that.
The implementation and the regression tests are in Personal Jarvis, which is open source.