Show the machine listening
Tags: voice, input, liveness
Typed input carries its own receipt: you press a key and the character appears. Continuous capture — voice, camera, screen, an agent watching a repository — carries none. A muted microphone, a silent room and a crashed listener all look exactly alike, and the user learns which one it was after they have finished talking.
Why this is a category problem, not a voice problem
Every discrete input confirms itself through the medium. The click depresses the button. The drag moves the thing. The keystroke lands. You never need a separate indicator that a text field is receiving text, because the text is the indicator.
Continuous capture breaks that, and it breaks it in the direction that costs most: the failure and the success look the same until the end. Nothing arrives to confirm anything, so the interface has to manufacture the confirmation, continuously, for as long as capture runs.
It reads as a voice-interface concern because that is where it was first solved. It isn't one. An agent watching a screen, a camera running pose detection, a background process watching a directory for changes — all of them are asking a person to produce input into something that gives no sign of receiving it.
Three states, and the middle one is the point
Most implementations ship two: listening and not listening. That is not enough to be useful, because it cannot distinguish the two situations a user actually needs told apart.
- Not capturing. Nothing is being taken in. This one is easy and everyone builds it.
- Capturing, receiving nothing. The microphone is muted, the room is too loud for the gate, the permission was revoked, the camera is covered. The product is trying and getting silence.
- Capturing, receiving you. Signal is arriving.
The middle state is the whole reason to build this. It is the one that tells someone their microphone is muted while they still have time to unmute it. Skip it, and a person can talk for a full minute into a product that is faithfully reporting that it is listening — which was true, and useless.
Derive it from the signal, not from a timer
This is the part that separates an indicator from a decoration, and it is worth being strict about.
A spinner that runs while capture is open proves that code is executing. It does not prove that anything is arriving, and it will keep spinning contentedly over a dead microphone. It is confident at precisely the moment it knows least.
A waveform drawn from the actual audio buffer proves something different: it moves when you speak and flattens when you stop. That flattening is the middle state, rendered for free. You do not have to detect the muted microphone as a special case; you have to render the signal honestly and let the user read the flat line.
The general form: bind a visual property to a live measurement of the input, not to the fact that capture is open. For audio it is amplitude. For a screen-watching agent it is the last thing it actually saw, named. For a file watcher it is the last event and when.
Which signal, and how loud
Intensity should scale with how much the user is actively producing. A waveform is right for the moment someone is dictating into the product and watching it happen. It is wrong for an agent quietly watching a repository all afternoon, where the same treatment would be a dancing distraction — there the requirement is that the indicator never disappears, not that it moves.
The other half is consent
For anything watching a screen, a room, or a camera, the capture indicator is not an affordance. It is the surface through which a person exercises a right to know whether they are being recorded.
Two consequences. It has to stay visible when the product is not in focus — backgrounded is exactly when it matters, and it is exactly when most implementations stop drawing it. And stopping has to be as visible as starting, because a capture you cannot obviously end is the thing that makes people quietly stop using ambient products without ever filing a complaint.
Grounded in
Poppy's dictation runs on the Web Speech API with a live waveform drawn on canvas from the audio itself, rather than an animation that starts when the microphone opens.
The reason is in the use case. The users are teachers, and the realistic moment of use is a few minutes between classes with their hands full — putting away one lesson, setting up the next, talking a parent email into existence in the gap. Fire-and-forget dictation is unusable in that window, and not because it is imprecise. It is unusable because if it was not listening, the ninety seconds they had are gone and they find out at the end, when there is no second attempt available. The waveform is what makes it worth starting.
Anti-patterns
- A spinner instead of a signal. It runs on a timer, proves nothing, and spins just as confidently over a dead microphone as a live one.
- A decorative waveform. A canned animation shaped like a signal is worse than no indicator at all: it is a specific, confident lie told at the exact moment the user is deciding whether this product can be trusted with the thing they are about to say.
- Two states. On and off cannot tell a muted microphone from a quiet room, which is the one distinction worth rendering.
- The indicator out of the line of sight. During dictation people watch the text appear. A microphone icon in a far corner is technically present and functionally absent.
- The indicator vanishing when backgrounded. For screen and ambient capture this inverts the requirement — it disappears exactly when the person most needs to know.
- No visible stop. Capture that cannot obviously be ended teaches people to end it by quitting the product.
The smallest version worth building
One canvas, one animation frame loop, and the analyser node's frequency data — bound to a visual property and drawn where the user is already looking. That is an afternoon.
Then the state everyone skips: if the level stays at zero for more than a second or so while capture is open, say so in words. "Not hearing anything — is your mic muted?" is the entire feature, and it is the difference between a person losing ten seconds and losing the only window they had.
Related patterns
- Streaming is a commitment — the same honesty requirement on the way out: don't render confidence over state the system doesn't hold.
- The character is a state machine — the persona carries the states of the process; this carries the states of the input, and on a voice product they are frequently the same rig.
- Design the failure state first — a muted microphone is a failure mode, and drawing it before the happy path is what produces the middle state at all.