About this reaction time test
A small, dependency-free page with one job: give you an honest millisecond reading of how fast you react to a colour change, and be upfront about everything that reading does and does not include.
Why it exists
Most online reaction time tests report a single confident number and stop there. Few explain that the figure is not pure biology — it is biology plus whatever your screen and input device add on top — and fewer still guard against the obvious way to cheat a click-and-time test: guessing when the signal is about to appear and clicking on a rhythm instead of actually reacting. This page exists to do both properly: a genuinely random wait, a too-soon check that discards a false start rather than rewarding it, and a clear explanation of what the resulting number can and cannot tell you.
How the timing is implemented
Two timestamps, one clock, no shortcuts:
- The moment the stage turns green, the script reads
performance.now()and keeps it as the "go" time. Nothing else runs before that read. - The click, tap, or key press that ends the wait arrives as a browser event carrying its own
event.timeStamp. - The reaction time is
event.timeStamp − goTime.
That subtraction is only meaningful because both values are the same kind of measurement: per the High Resolution Time specification, event.timeStamp is a DOMHighResTimeStamp sharing the same time origin as performance.now(). Mixing either of them with Date.now() — a wall-clock reading on a coarser, adjustable clock — would silently corrupt the result; this code never does. Clicking during the red "wait" phase is checked separately and treated as a false start: the pending go-timer is cancelled, nothing is measured, and the same attempt is offered again without being counted toward the run of five.
How it was verified
The pure timing and statistics functions have no dependency on the DOM, so they were exercised directly under Node before shipping:
| Check | Result |
|---|---|
| Random delay stays within the documented 1.5-4s window across thousands of draws | held |
| Elapsed time subtraction matches hand-computed expected values | exact |
| Elapsed time never reports negative even with an out-of-order timestamp | floored at 0 |
| Best/mean of a 5-value run matches manually computed statistics | exact |
| Millisecond formatting rounds rather than truncates | exact |
What could not be checked outside a real browser and real hardware is the one thing worth saying plainly: the actual millisecond figure a visitor sees depends on their specific screen and input device, and no amount of unit testing changes that — it is a property of the measurement, not a bug in this page.
How the site is built
- Static HTML, one small stylesheet, one small script. No framework, no build step, no bundler.
- Nothing is fetched from the network. No CDN script, no web font, no remote image; the favicon and header glyph are inline SVG. Once loaded, the page needs no further network access to run.
- System font stack — your operating system's own UI font, so nothing downloads and nothing shifts as the page loads.
- What you do here never leaves your device. The whole test runs in your browser. The only thing saved anywhere is a single all-time-best number in this browser's
localStorage, so it can be shown back to you later — it is never uploaded. - Dark mode follows your system setting via
prefers-color-scheme.
Scope and honesty about limits
This measures simple reaction time — one signal, one response — not choice reaction time, not reflexes under load, and not anything clinical. It cannot separate your own nervous system's speed from your screen's refresh delay or your mouse, trackpad, or touchscreen's input lag; every browser-based reaction time test reports that same combined number whether or not it says so. Treat your result as useful for tracking your own trend on the same device over time, not as a portable, device-independent measurement of "how fast you are."
If something here does not work as described, that is worth reporting — see contact.