← Brand Vantage · Web Developer
Hand Touch Bench

Voice scroll sensor
Microphone · no mouse

Appendix — input

Scrolling by saying so

Say “up” and this page scrolls up. Say “down” and it goes down. It keeps going until you say “stop” — because speech arrives with a delay of several hundred milliseconds, a word is a far better fit for setting a direction than for nudging a distance. “Faster” and “slower” change pace, “top” and “bottom” jump to the ends. Every stage is on the right, from microphone level to the pixels per second going into scrollTop.

01

A word is not a button

Speech recognition returns its first guess a few hundred milliseconds after you start speaking, and revises it afterwards. Anything mapped to a word inherits that delay, so commands here set a state that persists — a direction to keep going in — rather than a one-shot nudge you would have to repeat.

02

Interim results are the whole game

Waiting for a final transcript costs roughly a second, most of it spent waiting for you to stop talking. Acting on interim results cuts that to a few hundred milliseconds — at the price of guesses that get revised under you, which is why every word is consumed exactly once.

03

Continuous recognition is not continuous

Chrome ends the session on its own after a stretch of silence, quietly, with no error. A voice interface that does not restart itself appears to work perfectly and then stops responding a minute later, which is a far worse failure than never starting.

04

The meter is local, the words are not

The level bars come from an AnalyserNode running on your machine, which is why they respond instantly. The transcript comes back from a server. Seeing the bars move while no words appear tells you which half of the chain is struggling.

05

Every word is consumed once

Interim results arrive as a growing string — "up", then "up down" — re-delivering earlier words each time. Counting how many tokens of each result have already been acted on is what stops a single “up” from firing five times as the guess is refined.

06

Stop has to be the easy word

It is the one command you may need in a hurry, so it is matched from the widest set of synonyms, and Esc does the same thing without speaking. A control you cannot cancel quickly is not a control.

End — say “top” to go back

MicrophoneOff

Floor —

StateIdle
Last word—
Confidence—
Latency—
Direction—
Velocity—
Progress0%
Signal / noise—
Gate—
Noise rejects0
Alternatives—
Restarts0
Heard —

Latency
Matching

Waits for the final transcript and accepts near misses

Noise gate
Suppression

Commands need real speech above the room's noise floor

How it works — the signal chain

Six stages between a word and a scrollbar

Two paths leave the microphone. One stays on this machine and drives the level meter instantly; the other goes to a recognition service and comes back as text, several hundred milliseconds later. Only the second one can tell “up” from “down”.

Stage 01

Capture

One getUserMedia stream, shared. The recogniser would open its own, but then the meter could not show you whether the microphone was actually picking anything up.

out → MediaStream
Stage 02

Analyse · local

An AnalyserNode gives a running RMS of the waveform, drawn as bars. This path never leaves the machine and never waits for anything.

out → level · 0–1
Stage 03

Recognise · remote

SpeechRecognition in continuous mode with interim results on. This is the stage that needs a network and the stage that sets the latency floor.

out → transcript
Stage 04

Tokenise

The transcript is split into words and matched against a synonym table. Matching whole tokens rather than searching the string keeps “up” from firing inside “upset” or “supper”.

out → tokens[]
Stage 05

Consume once

Each result index remembers how many of its tokens have already been acted on, so words re-delivered by later interim updates are skipped rather than re-fired.

out → command
Stage 06

Integrate

The command sets a direction; velocity eases toward direction × speed and accumulates into scrollTop, stopping itself at either end.

out → scrollTop · px

Vocabulary

What it listens for

Each command accepts several words, because recognisers mishear short ones constantly and a single accepted spelling makes the interface feel broken rather than approximate.

SayEffect
up · upward, upwards, rise, higherScroll up until told otherwise
down · downward, downwards, lowerScroll down until told otherwise
stop · halt, freeze, pause, wait, enoughStop — also bound to Esc
faster · quicker, quickly, speedIncrease speed by one step
slower · slow, slowly, gentleDecrease speed by one step
top · beginning, start, homeGlide to the very top
bottom · end, finishGlide to the very bottom

Accuracy

The words this needs are the words recognisers are worst at

“Up” and “down” are single syllables carrying almost no acoustic information, and the recogniser is running a language model built for dictation — one that has never seen anyone say “up” on its own and would much rather hear “app”, “cup” or “hop”, all of which are more probable in ordinary English. Four things push back against that, and they stack.

Fix 01

Read every alternative

The recogniser is asked for six guesses instead of one, and all six are scanned for a command. For short words the right answer is very often the second or third — it simply scored lower than a longer real word. The Alternatives readout shows which one won: a pick consistently above #1 means the top guess cannot be trusted.

was → 1 guess · now → 6
Fix 02

Wait for the final transcript

Interim results are the first thing the recogniser thinks of, and they get revised constantly. Acting only on finals costs roughly half a second and removes most misfires. That is the Latency switch, and it now defaults to Accurate — switch it to Fast if you would rather have the speed back.

cost → ~500ms
Fix 03

Accept near misses

A table of substitutions actually observed in testing — app, cup, hop for up; dawn, town, done for down — plus edit-distance matching for longer words. Only words of four letters or more get fuzzy treatment: at three letters a single edit reaches half the vocabulary.

switch → Forgiving / Exact
Fix 04

Protect “stop”

“Stop” and “top” differ by one dropped consonant, and confusing them is uniquely bad: you ask it to halt and it flings you to the top of the document. So top is only honoured when no alternative heard stop. The dangerous direction of the ambiguity is closed off.

tie → stop wins

There is also a plain setting worth checking before blaming any of the above: the language. Matching an Indian or British speaker against en-US loses accuracy on vowels for no reason at all, and “down” is mostly vowel. The page now starts on your browser's own English variant when it has one, and the selector sits next to the microphone button.

Noise

You cannot clean the audio the recogniser hears

This is the constraint everything here is built around. SpeechRecognition opens its own microphone capture inside the browser and offers no way to hand it a processed stream — there is no input to attach a filter graph to. So the usual approach, denoise the audio and pass it along, is simply not available. What is available is three things that work around it.

Layer 01

Ask the browser to do it

The capture is opened with noiseSuppression, echoCancellation and autoGainControl named explicitly rather than left to defaults. These run inside WebRTC's own processing chain, upstream of everything, and are the only denoising that reaches the recogniser at all.

reaches → recogniser
Layer 02

Band-limit before measuring

A highpass at 85 Hz and a lowpass at 4 kHz bracket the speech band. Fan hum, traffic and desk thumps live below that and would otherwise sit in the measured noise floor as though they were voice — pushing the gate threshold up until quiet words stopped registering.

reaches → meter, gate
Layer 03

Gate the commands, not the audio

The room is sampled for 1.3 s at startup and its 60th percentile becomes the noise floor. A command is only accepted if speech rose a set margin above that floor recently enough to have produced the transcript. Noise the recogniser hallucinated words from is counted and dropped.

reaches → decisions

The gate consults when speech last happened rather than the level at this instant, because a final transcript lands several hundred milliseconds after the speaker stopped — testing the microphone at the moment a command arrives would reject every real word. The floor also tracks slowly downward whenever the input sits below it, so a room that goes quiet is followed automatically, while speech can never drag it up.

Reading the numbers. Signal / noise shows how far above the floor you currently are — if speaking normally does not push it past the gate threshold, move closer or recalibrate. Noise rejects counts commands dropped for having no speech behind them; a number that climbs while the room is empty means the floor was calibrated during noise, and Recalibrate room fixes it.

The failure everyone ships

Continuous recognition stops on its own

Setting continuous = true does not mean the session runs forever. Chrome ends it after a stretch of silence and fires onend with no error at all — so a voice interface that does not restart itself works beautifully for about a minute and then goes deaf, with nothing on screen to say why.

Restarting naively creates the opposite failure. If the microphone is blocked or the network is down, onend fires immediately every time, and an unconditional restart becomes a tight loop hammering the service. So restarts here are counted and spaced: a normal silence timeout restarts almost at once, while repeated ends inside a second back off progressively. The Restarts readout exposes the count, because a number that climbs steadily while you are talking is the signature of a recogniser that cannot reach its service.

This one is not private, and the difference matters. The hand sensor ran its model entirely on your machine — no video ever left the page. Speech recognition in Chrome does not work that way: audio is streamed to Google's servers for transcription, which is why it needs a network connection and why it can stop working without warning. The level meter is local, the words are not. Nothing is recorded or stored by this page, but the audio does leave your machine while the microphone is on.

The microphone never starts on its own. Nothing is requested until you press the button, Esc or the corner Stop ends both the stream and the recogniser, and the wheel, trackpad and arrow keys keep working throughout. Recognition needs Chrome or Edge — Firefox does not implement the API, and the page says so rather than appearing broken.

Idle