As a PC gamer, I've always hated installing 500MB of bloatware (looking at you, Razer Synapse and G-Hub) just to verify if my mouse is actually running at its advertised 1000Hz polling rate.
I asked myself: Why can't I just test this in the browser?
The common answer is: "You can't. The browser is too slow. requestAnimationFrame is capped at your monitor's refresh rate (usually 60Hz or 144Hz)."
Challenge accepted. 🛠️
I spent the last few weeks building https://hardwaretest.org/mouse-polling-rate-test/, a privacy-first diagnostic suite. The hardest part was getting accurate, raw input readings that bypass the visual rendering loop.
Here is how I solved the "1000Hz Problem" in Vanilla JS.
The Problem: Visuals vs. Inputs
Most web animations rely on requestAnimationFrame (rAF). If your monitor is 60Hz, rAF fires every ~16.6ms. If you try to measure a 1000Hz mouse (which sends data every 1ms) inside a rAF loop, you will miss 94% of the data.
We need to decouple the Input Thread from the Render Thread.
The Solution: pointermove + performance.now()
We can't rely on the screen painting. We need to listen to the raw pointermove event, which fires much more frequently than the screen updates in modern browsers (Chrome/Edge handle this effectively via "Coalesced Events" logic, though we want the raw stream).
But standard Date.now() isn't precise enough. It gives us milliseconds. For 1000Hz, we need microseconds.
Enter the High Resolution Time API.
The Code
Here is the simplified logic of how I calculate the instant Polling Rate (Hz):
JavaScript
let lastEventTime = 0;
let pollingRateSamples = [];
window.addEventListener('pointermove', (event) => {
// 1. Get the microsecond-precise timestamp
const currentEventTime = performance.now();
// 2. Calculate the delta (time between two events)
const timeDelta = currentEventTime - lastEventTime;
// 3. Convert to Frequency (Hz)
// Hz = 1000ms / delta
const instantaneousHz = 1000 / timeDelta;
// 4. Filter out noise (idle or super slow movements)
if (instantaneousHz > 10 && instantaneousHz < 8000) {
pollingRateSamples.push(instantaneousHz);
}
lastEventTime = currentEventTime;
// NOTE: Do NOT update the DOM here!
// Updating the UI every 1ms will kill the browser performance.
// Store the data, and visualize it in a separate requestAnimationFrame loop.
});
The "Event Loop" Trap
The tricky part is that browsers try to "group" events to save battery.
If you run a heavy operation inside that pointermove listener, the browser will start throttling the event firing rate to match the frame rate.
The trick is:
Keep the event listener extremely lightweight (just math and array pushing).
De-couple the visualization. Use a separate requestAnimationFrame loop to read from the pollingRateSamples array and draw the graph.
The Result
By separating the input capture from the rendering, I managed to get consistent readings matching local software:
You can try the live demo here: Mouse Polling Rate Test
It also includes a Gamepad Tester (for stick drift) and a Dead Pixel test, all running locally in the browser.
Discussion 💬
I built this to prove that modern Web APIs are powerful enough to replace most "utility" desktop software.
What do you think? Are we at a point where we can finally uninstall those 500MB driver suites, or will native apps always be king for hardware interaction?
Let me know in the comments! 👇
Top comments (0)