About this mouse tester
A small, dependency-free page for one job: showing you exactly what your browser is receiving from your mouse — which button, which direction, how far apart in time — without sending any of it anywhere.
Why it exists
Most "is my mouse broken" questions have a genuinely testable answer, but the honest answer is often more nuanced than a pass/fail light. A worn switch doesn't always fail outright — it chatters intermittently. A side button doesn't always stay dead — it drops out under specific pressure angles. A polling-rate claim on a product page isn't something most people have a way to independently sanity-check at all. This page tries to make the actual, timestamped browser events visible, so you are looking at evidence rather than a vibe.
How each test is implemented
- Buttons.
event.buttononpointerdown/pointerupidentifies which of the five conventional indices (0-4) was pressed.contextmenuis prevented only inside the test pad so a right-click registers instead of opening a menu.auxclickcatches the completed click for non-primary buttons on browsers where plainclickonly ever fires for the left button. - Scroll. The
wheelevent'sdeltaYsign gives direction; its magnitude is shown as-is. The test pad callspreventDefault()so testing the wheel doesn't also scroll the page underneath it. - Chatter. Every left
click'sevent.timeStampis compared to the previous one. A gap shorter than the threshold (35ms by default) is flagged, on the reasoning that no deliberate human click-release-click cycle is that fast — see the main page for the reasoning behind that number. - Polling rate. While you move the mouse over the test pad, consecutive
pointermovetimestamps within a rolling window are averaged into an interval, inverted into a Hz figure, and rounded to the nearest of 125/250/500/1000 — the common values real hardware ships at. The honest caveat about browser event coalescing is on the main page and repeated in the tool itself.
How the logic was checked
The parts of this tool that are pure arithmetic — no DOM, no browser — live in a separate module and were run against known cases before release:
| Case | Expected | Result |
|---|---|---|
| event.button 0 / 1 / 2 / 3 / 4 | left / middle / right / back / forward | match |
| 10ms gap between two left clicks | flagged as chatter | match |
| 300ms gap between two left clicks | normal, not flagged | match |
| A run of timestamps ~1ms apart | buckets to ~1000Hz | match |
| A run of timestamps ~8ms apart | buckets to ~125Hz | match |
Edge cases were checked too: fewer than two timestamps returns "not enough data" rather than a fabricated number; a negative or non-numeric gap returns nothing rather than a false verdict; an unrecognised button index reports "unknown" rather than guessing.
How the site is built
- Static HTML, one small stylesheet, two small scripts. No framework, no build step, no bundler.
- Nothing leaves your device. Every event this page reads is processed locally; none of it is uploaded, logged, or stored. The favicon is an inline SVG data URI, so even that needs no request.
- System font stack — nothing to download, nothing to shift as the page loads.
- Dark mode follows your system setting via
prefers-color-scheme.
Scope and honesty about limits
This tool reads standard DOM events — event.button, event.deltaY, event.timeStamp — the same properties any web page can read. It cannot see anything a browser itself cannot see: it has no access to raw USB HID reports, DPI settings, sensor lift-off distance, or firmware-level diagnostics. The polling-rate figure in particular is explicitly an approximation shaped by browser event coalescing, not a hardware measurement — see the FAQ on the main page for why.
It is not a substitute for a manufacturer's own configuration software where one exists, and it cannot tell you *why* a button or switch is misbehaving — only that the browser did or didn't receive the event you expected.