C.W.K.
Stream
Lesson 02 of 04 · published

Anatomy of One Dictation

~12 min · loop-anatomy, target-snapshot, pre-roll, capture

Level 0Unlit
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"The two riskiest moments are the first millisecond and the last. Everything else is bookkeeping."

Down, Speak, Up

One dictation splits cleanly at the hotkey. On key-down you capture two things at once: a target snapshot (who has focus right now) and the audio stream (with the mic already warm). On key-up you finalize the transcript, clean it, and insert it into the target you snapshotted. The controller is small on purpose:

DictationController.start(mode):
    snapshot = TargetCapture.snapshot()   # frontmost app, focused element, selection
    Audio.start()                          # begin capture with a short pre-roll

DictationController.stop():
    audio = Audio.finish()                 # add a short tail so last syllable survives
    text  = STT.finalize(audio)
    clean = Cleanup.run(text, snapshot)
    Insertion.insert(clean, into: snapshot)

The First Millisecond: Pre-Roll

Humans start talking before their finger finishes pressing. If capture begins exactly at key-down, the first syllable is already gone — "send it" becomes "end it." The fix is a pre-roll: keep a short rolling buffer so the audio you finalize includes the ~half-second before the key registered. But pre-roll fights privacy — a true rolling buffer means the mic is always live between dictations. Firekeeper's honest tradeoff is to request mic permission at launch and start the engine fast rather than keep an always-on buffer, because a visible-recording-only rule beats a hidden always-listening one.

The Last Millisecond: The Tail

The mirror problem is at the end. People release the key the instant they finish the last word — sometimes a hair early. Cut the audio exactly at key-up and the final syllable clips. So capture keeps running for a short tail (Firekeeper uses roughly a quarter-second) after key-up before it finalizes. Two tiny time-shifts, front and back, are the difference between "transcription is broken" and "transcription is perfect."

Guard the edges of the recording, not just the middle. STT quality in the middle of an utterance is the model's job. The first and last syllables are the app's job — pre-roll protects the start, a release tail protects the end. Get the edges wrong and users blame the model for a bug you built.
Never let a silence detector eat the first word. Voice-activity detection is great for trimming dead air and finding endpoints — but if it runs before pre-roll, it can decide the quiet lead-in is 'nothing' and discard the very syllable you needed. Order matters: keep the pre-roll, then trim silence, never the reverse.

Code

The core controller: capture the target, then the audio·swift
func start(mode: DictationMode) {
    // Snapshot the target BEFORE any overlay can move focus.
    self.snapshot = targetCapture.snapshot()   // app, element, selection
    audio.start()                              // engine already warm from launch
    phase = .recording
}

func stop() async {
    // Keep a short tail so the last syllable isn't clipped.
    let clip = await audio.finish(tail: .milliseconds(280))
    guard clip.hasSpeech else { return }       // silence gate: don't call STT on nothing
    let raw = await stt.finalize(clip)
    let result = await cleanup.run(raw, context: snapshot)
    await insertion.insert(result, into: snapshot)
}

External links

Exercise

Record yourself saying a short phrase and deliberately release 'stop' the instant you finish the last word. Play it back. Did the final consonant survive? Now imagine there's no release tail in the code — describe the exact bug a user would report, and why they'd blame the wrong component.
Hint
Without a tail, the user reports 'it keeps cutting off my last word' and blames the speech model. But the model never heard the last word — the app stopped the mic too early. That misattribution is why edge-guarding is an app responsibility, not a model tuning problem.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.