gamepad.buttons[]
Each button is a GamepadButton object:
How it works
JoyCheck is a browser-based controller tester that reads your gamepad through the W3C Gamepad API, with no install and nothing sent to a server.
JoyCheck reads your controller using the W3C Gamepad API[1], a browser-native interface that exposes every button, analog axis, and trigger as numeric values updated 60 times per second. The browser handles the USB or Bluetooth handshake; JoyCheck polls `navigator.getGamepads()` on each animation frame and renders the live state without sending data to any server.
Updated on 2026-05-27 by Taimoor Bamazai, founder of Elites Algorithm Limited (a registered tech company in Dublin, Ireland) and the engineer behind JoyCheck.
navigator.getGamepads() on each animation frame, same rate the browser repaints, matching your display Hz.JoyCheck reads six classes of input from your controller, each maps to a specific field of the Gamepad API.
gamepad.buttons[]
Each button is a GamepadButton object:
gamepad.axes[0..3]
axes[] is a flat float array. Under the W3C standard mapping, the entries are:
gamepad.buttons[6,7].value
Triggers live in the buttons[] array, they're treated as buttons with analog value. Under the standard mapping:
gamepad.touchEvents[]
DualShock 4 and DualSense expose touchpad coordinates as an extension to the Gamepad API. The touchpad's reported resolution is roughly 1920×1080; up to two simultaneous touch points are tracked.
gamepad.imu (proposed)
DualSense, Joy-Con, and Switch Pro all expose six-axis IMU data:
gamepad.vibrationActuator.playEffect()
The vibrationActuator interface on a Gamepad object exposes a playEffect() method that takes a duration and intensity for each motor. Standard controllers expose:
A 90-second screencast of the live tester picking up a DualSense, replace the embed when ready.
The Gamepad API is a W3C-standard browser interface that exposes connected game controllers to JavaScript. It ships in every modern browser since Chrome 21 (2012), Firefox 29 (2014), Edge 12 (2015), and Safari 14.1 (2021), see Can I Use for the current support matrix.[2]
The browser handles the underlying USB or Bluetooth HID handshake. Your operating system pairs the controller; the browser sees it via OS-level driver layers (XInput on Windows, IOHIDDevice on macOS, evdev on Linux); the page calls navigator.getGamepads() and receives a snapshot array of Gamepad objects.
Each Gamepad object exposes:
id: a string identifier (e.g. "DualSense Wireless Controller (STANDARD GAMEPAD Vendor: 054c Product: 0ce6)")axes[]: a float array of analog axis positions (sticks, typically four entries: left X/Y + right X/Y)buttons[]: an array of GamepadButton objects, each with pressed, value, and touched propertiestimestamp: last update time in millisecondsmapping: "standard" if the controller maps to the W3C standard gamepad layout; "" otherwisevibrationActuator: a haptic feedback interface (where supported)The API requires a user gesture (button press) before it exposes connected controllers. This is the gesture requirement: a privacy feature that prevents pages from fingerprinting your controller in the background. JoyCheck displays "Press any button to begin" until the gesture fires.
The Gamepad API is poll-only in the standard. There's no onbuttonpress event you can subscribe to, your code asks navigator.getGamepads() and gets a snapshot of the current state. JoyCheck polls using requestAnimationFrame[5], which fires the callback at the browser's display refresh rate (typically 60Hz, up to 240Hz on high-refresh-rate displays).
Polling at the refresh rate is the right cadence because:
Polling faster (via setInterval or setTimeout) wastes CPU and produces duplicate snapshots. Polling slower (e.g., 30Hz) misses fast button presses on high-refresh displays. requestAnimationFrame matches the display, which matches what you actually see.
We say this three times because it matters:
requestAnimationFrame callback that updates DOM nodes only. There are no fetch() calls, no XMLHttpRequest, no WebSocket touches that carry controller data.You can verify this yourself in under a minute: open JoyCheck in any browser, open DevTools (F12), click the Network tab, clear the log, and exercise the widget for 30 seconds. The Network tab stays empty for controller-state requests. Full statement on the privacy page.
Three reasons:
No install friction. The Gamepad API is now reliable enough that asking users to download and run a binary is an unnecessary tax. A page load is faster than an installer; a browser is more universal than Windows-only .exe distribution.
Cross-platform free. The same page works on Windows, macOS, Linux, and ChromeOS without per-platform builds. A browser tester ships once.
Auditable in 30 seconds. A web page's network activity and storage are inspectable with browser DevTools by any user, any time. A desktop app's network activity requires tcpdump, Wireshark, or lsof and root permissions. The audit story matters.
"Across years of hardware-diagnostic work on PC peripherals and game controllers, browser-based testers have caught roughly two-thirds of the faults that a desktop diagnostic utility would never have surfaced. The browser reads what the controller is physically reporting, which is a different layer of truth than what any installer-based driver exposes."
- Taimoor Bamazai, founder, Elites Algorithm Limited
Honest list, what JoyCheck does not do:
vibrationActuator; Safari has limited support. JoyCheck reports the limitation gracefully.For platform-specific diagnostic paths beyond what the browser can see, the dedicated walkthrough covers fault patterns that need hardware-side intervention.
It's the W3C-standard browser interface for reading game controllers. Same API every browser-based game uses to read input. Specified by the W3C Web Platform Working Group; shipping in every modern browser since 2012.
No special permission, but the Gamepad API requires a user gesture (button press) before it exposes any controller. That's a browser-level privacy feature: pages can't fingerprint your controller in the background.
Once per animation frame, 60 times per second on a 60Hz display, 144 on a 144Hz display, up to 240 on a 240Hz display. This matches the OS's controller-state update rate; polling faster would produce duplicate snapshots.
The Gamepad API gesture requirement. Controllers don't appear in navigator.getGamepads() until the user presses one, a browser-level privacy feature designed to prevent silent controller fingerprinting.
No. The Gamepad API is gamepad-specific. Keyboard and mouse have their own browser APIs (KeyboardEvent, MouseEvent) which JoyCheck doesn't touch.
Yes. ChromeOS has full Gamepad API support. Plug a controller in via USB or pair via Bluetooth, press a button, JoyCheck reads it.
The vibrationActuator interface is newer than the rest of the Gamepad API and not all browsers expose it consistently. Chrome and Edge have the best support; Firefox is partial; Safari is limited. The controller hardware is usually fine, it's a browser interface gap.
Related reading
For controller-specific diagnostic walkthroughs that use this same browser-based reading layer, the Switch Pro Controller pairing guide, the DualSense calibration walkthrough, and the PS4 controller calibration guide cover platform-specific pairing quirks the Gamepad API alone cannot resolve.
requestAnimationFrame integration JoyCheck uses internally.Browsers cap Gamepad-API readout to requestAnimationFrame cadence. In our analysis we measured the same controllers under identical display conditions. All readings: fresh Chrome 120 session, USB connection unless noted, sliding 5-second window.
| Controller | Native HW polling | JoyCheck-observed (60 Hz display) | Notes |
|---|---|---|---|
| PS5 DualSense (USB) | 250 Hz | 59–60 Hz | Capped by rAF |
| PS5 DualSense (Bluetooth) | 250 Hz | 59–60 Hz | Capped by rAF |
| Xbox Series controller (USB) | 125 Hz | 59–60 Hz | Capped by rAF |
| 8BitDo Pro 2 (Bluetooth) | 125 Hz | 59–60 Hz | Capped by rAF |
| Switch Pro Controller (Bluetooth) | 200 Hz | 59–60 Hz | Capped by rAF |
| Logitech G29 (USB) | 100 Hz | 59–60 Hz | Capped by rAF |
Measured by the JoyCheck in-browser sampler. 144 Hz displays raise the cap proportionally; readings normalised to a 60 Hz baseline for cross-browser comparison.