About this gamepad tester
A small, dependency-free page for one job: reading whatever your browser's Gamepad API reports from a connected controller, and showing it to you plainly enough to tell a working pad from a failing one.
Why it exists
"Is this button actually dead, or did I just miss it?" and "is this stick really drifting, or am I imagining it?" are both hard to answer by feel alone, especially seconds before a return window closes on a secondhand controller. This tool exists to turn both questions into something you can look at: every button's live pressed state and analog value, both sticks' exact position with no deadzone hiding a small offset, and a straightforward attempt at flagging drift rather than leaving you to eyeball a twitchy dot.
How it's built
The maths — stick magnitude, the "has this stick been still, and for how long" drift heuristic, and standard-mapping button labels — lives in a plain data-and-functions file with a module.exports hatch, so it can run and be sanity-checked under Node without a browser or a physical controller attached. The page itself builds one panel per connected controller with document.createElement, then drives it from a single requestAnimationFrame loop that calls navigator.getGamepads() once per frame, updates every button, stick and D-pad element directly, and re-scans the returned list each frame so it also notices a pad disconnecting even in browsers that don't reliably fire the gamepaddisconnected event. That loop cancels itself automatically once no controller is left connected, and again on pagehide, so it never keeps running in a background tab.
The drift flag works by tracking, per stick, the last time either axis moved by more than a small amount. Once a stick has gone still for roughly a second, its current reading becomes the "resting position" shown as a dim marker; if that resting reading's magnitude is still above a small threshold, the badge appears. It is a plain, explainable rule — not a manufacturer diagnostic — and the page says so.
What it deliberately doesn't do
- It doesn't claim to see raw hardware state. By the time
navigator.getGamepads()returns anything, your operating system has already turned electrical signals into normalised numbers. A fault the OS driver silently smooths over won't appear here. - It doesn't promise rumble will work. Vibration support depends on the browser implementing
vibrationActuatorat all, the controller reporting one over the connection in use, and the specific effect type being supported — three independent things that can each fail for reasons unrelated to whether the controller's motors are fine. - It doesn't guess button names for non-standard mappings. If a controller doesn't report the W3C "standard" gamepad mapping, this tool shows a plain numbered button list instead of invented labels it can't verify.
- It doesn't measure polling rate. That number isn't part of the Gamepad API at all; this page's own refresh rate is tied to your display, not to the controller.
- It doesn't send anything anywhere. Every reading is produced by, and discarded in, your own browser. There is no server call, no analytics beacon tied to a button press, and nothing kept after the page closes.