State of the Art: Local/Offline STT & Dictation Post-Processing (2026-08-07)¶
Date: 2026-08-07 · Tier: design/ — public engineering research Companion: gaze / EMG / BCI / multimodal input SoA · voice-dictation market landscape · ADR-002: Dual-Stack STT Routing · ADR-v2-129: Killer Features 10x
All claims cited; numbers marked "~" are approximate or vendor/community-reported rather than independently verified.
1. STT engine landscape (beyond faster-whisper)¶
| Engine | Avg WER | CPU speed (int8) | Streaming | Word ts | License | Python path | Size |
|---|---|---|---|---|---|---|---|
| faster-whisper small.en | ~8–9% | ~8x realtime | LocalAgreement (app-side) | Yes | MIT | native | ~460 MB |
| whisper-large-v3-turbo (CT2 int8) | ~7.5% short-form | heavy on CPU | No | Yes | MIT | faster-whisper | ~1.5 GB |
| Distil-Whisper large-v3 | within ~1% of large-v3 | ~6x faster than large-v3 | No | Yes | MIT | faster-whisper | ~750 MB |
| NVIDIA Parakeet TDT 0.6B v2 (En) / v3 (25 EU langs) | 6.32–6.34% — beats large-v3 (7.44%) | ~30x realtime CPU (~4x whisper-small); no silence hallucination | Pseudo only (chunked, degrades) | Yes (TDT native) | CC-BY-4.0 | onnx-asr (pure Python, no torch) or sherpa-onnx int8 | ~600 MB fp |
| Moonshine v2 (2026-02) | Small-stream 7.84% (123 MB), Medium-stream 6.65% (245 MB) | edge-CPU design; 50–258 ms latency | Yes — native streaming encoder | Yes | MIT (English); non-En = non-commercial | ONNX; sherpa-onnx quantized | 26–245 MB |
| Kyutai STT (1b-en_fr / 2.6b-en) | competitive | 1B feasible on strong CPU | Yes — token-level, fixed 0.5 s delay, built-in semantic VAD | Yes | CC-BY-4.0 | moshi Python/Rust/MLX | 1B/2.6B |
| Canary-Qwen 2.5B | 5.63% | too heavy for CPU dictation | No | Yes | CC-BY-4.0 | NeMo | 2.5B |
| SenseVoice-Small | strong zh/yue/en/ja/ko, NAR very fast | very fast | No (chunked) | limited | Apache-2.0 | funasr / sherpa-onnx | ~230 MB |
| Vosk (Kaldi) | worse than whisper-small | realtime on RPi | Yes (true) | Yes | Apache-2.0 | vosk-api | 40 MB–1 GB |
Headline of 2025–2026: Parakeet TDT 0.6B changed the local-dictation calculus — better-than-large-v3 accuracy at less-than-small.en CPU cost, no hallucination on silence, a permissive license, and pure-Python inference via onnx-asr. Several 2025–26 open-source dictation apps added it (Handy, OpenWhispr, MacParakeet, parakeet-mlx). Caveat: sherpa-onnx maintainers state TDT is not designed for true streaming (issue #2918) — but a hold-to-talk, batch-per-burst pipeline needs fast batch, which is exactly Parakeet's sweet spot.
Key sources: HF nvidia/parakeet-tdt-0.6b-v3 · snailtext.app/blog/whisper-vs-parakeet-tdt · northflank.com 2026 STT benchmarks · arxiv.org/abs/2602.12241 (Moonshine v2) · github.com/moonshine-ai/moonshine · kyutai.org/stt · github.com/kyutai-labs/delayed-streams-modeling · k2-fsa.github.io/sherpa/onnx · github.com/istupakov/onnx-asr · marktechpost.com 2026-07-23 ASR roundup · github.com/SYSTRAN/faster-whisper/issues/1030.
2. Streaming / low-latency dictation¶
Commercial reference points: Aqua Voice reports starting to listen in <50 ms and inserting text in 450 ms–1 s (live as-you-speak display); Wispr Flow is cloud-only, 1–3 s to final formatted text. "Instant-feeling" in this space generally means live partial text during speech plus final text within roughly 500 ms of key release.
Local approaches, ranked by cost:
- Fast batch on release (Parakeet TDT) — at ~30x realtime CPU, a 5 s burst decodes in ~170 ms. A hold-to-talk model reaches that bar with no streaming machinery — the cheapest win.
- Moonshine v2 native streaming — a sliding-window-attention encoder with cached decoder state gives incremental cost per chunk: 148 ms (Small) / 258 ms (Medium) measured latency, and its streaming variants beat their batch siblings on WER. A modern replacement for LocalAgreement-style re-decoding of the whole buffer each tick.
- Kyutai delayed-streams — elegant (fixed 500 ms delay, built-in semantic VAD that could replace an RMS gate), but 1B params and Rust-recommended serving. Worth watching, not yet adopting.
- Parakeet chunked pseudo-streaming — degrades at chunk boundaries; not recommended.
3. Hotword / contextual biasing¶
- Whisper's
initial_promptmechanism is the weakest form of biasing available: a 224-token cap, end-weighted attention, a first-30-second-window-only effect, and hallucination risk with dense lists (OpenAI prompting guide; arXiv 2502.11572). - The current state of the art is decode-time shallow-fusion phrase boosting on CTC/Transducer models. NVIDIA TurboBias (arXiv 2508.07014, in NeMo word boosting) rescales token scores against a boosting tree during decode — no retraining, up to 20K phrases, no measured speed penalty, and it works with Parakeet TDT. CTC-WS is the lighter CTC alternative.
- A practical path for a project on this stack: sherpa-onnx exposes transducer hotwords directly; on the
onnx-asrpath, edit-distance post-correction from a vocabulary list is the fallback (arXiv 2410.18363). Either way, moving personal vocabulary frominitial_promptto real boosting removes both the 224-token cap and the hallucination tax.
4. Local LLM post-processing for dictation¶
Laptop-CPU (no GPU), Q4 GGUF via llama.cpp, 2026:
| Model | Size (Q4) | CPU tok/s (laptop-class) | Notes |
|---|---|---|---|
| LFM2.5-350M / 230M (Liquid) | 0.2–0.3 GB | fast (230M: ~42 tok/s on RPi 5) | IFEval 71.7, reported ahead of Qwen3.5-0.8B and Gemma-3-1B; llama.cpp/ONNX/MLX |
| LFM2.5-2.6B | ~2.5 GB | ~113 tok/s on Ryzen AI Max+ 395 | leads its size class on instruction-following in vendor benchmarks |
| Phi-4-mini 3.8B | ~2.3 GB | ~12 tok/s | |
| Gemma 3n / 4 E2B | ~1.5 GB | ~15 tok/s | good multilingual coverage |
| Qwen3-4B-Instruct-2507 | ~2.4 GB | ~8–12 tok/s | Apache-2.0, best quality/size tradeoff observed for rewrite tasks |
- A dictation cleanup pass typically emits ~30–80 tokens. At 12 tok/s that is 3–7 s (too slow to enable by default); at LFM2.5 speeds it drops under 1 s. Speed, not quality, is the binding constraint on CPU for a default-on cleanup step.
- Structured voice edits ("replace X with Y", "make that a bullet list") are common in cloud commercial tools. The proven local pattern is: classify edit intent → a small LLM emits a constrained JSON/GBNF edit op → apply it deterministically.
5. On-device personalization¶
- Whisper LoRA fine-tuning: large-v3 needs >30 GB VRAM (out of reach for a laptop); small/medium fits an 8–16 GB consumer GPU with real gains (−2.2 WER multilingual; larger for atypical speech with ~1.4 h of data). CPU-only training is impractical as of 2026. Worth watching: zeroth-order fine-tuning approaches (arXiv 2512.01267).
- An evidence-backed ordering of personalization ROI: (1) decode-time biasing lists, (2) corpus-mined prompt/vocabulary, (3) post-hoc correction from stored history, (4) LoRA fine-tuning last, reserved for GPU-equipped users with atypical speech or heavy domain jargon.
Recommendations at the time of this study¶
- A higher-accuracy optional engine: Parakeet TDT 0.6B v2/v3 via
onnx-asrint8 — better WER than large-v3 at roughly 4x whisper-small CPU speed, and it removes the class of hallucinated tokens on silent audio at the source. Fits a batch-per-burst pipeline well. - A streaming engine for live preview: Moonshine v2 Small/Medium Streaming (MIT, ONNX/sherpa-onnx) as a modern replacement for LocalAgreement-style re-decoding. Kyutai worth watching for its built-in semantic VAD.
- Post-processing: two presets — a small/fast model for sub-second cleanup on any CPU, and a larger model for quality — plus GBNF-constrained structured edit operations for voice-driven text edits.
What this study fed into¶
This study, together with the gaze/EMG/BCI SoA and a full codebase reachability audit, was the direct research input to ADR-v2-129, which added the pluggable SttEngine protocol and the Parakeet backend (item 2.2 there). The stt/factory.py Protocol seam and [stt] engine = whisper | parakeet configuration described in architecture.md are the shipped result of recommendation 1 above.