Measuring what the browser leaks about your hardware through mouse, display, GPU, audio, and system signals.
Measures intervals between requestAnimationFrame callbacks to derive your monitor's refresh rate. Runs passively on page load. Rates above 60Hz strongly suggest a gaming monitor.
The browser fires pointermove once per display frame. On multi-monitor setups this reports the primary monitor's rate regardless of which screen the window is on. If it differs from the rAF rate, you have multiple monitors at different refresh rates.
Each pointermove fires once per display frame, but the mouse hardware may have polled many times during that frame. getCoalescedEvents() unpacks the batched raw events. We take the max count seen in a single frame and multiply by the display rate. Default mice poll at 125Hz. Gaming mice run at 500-8000Hz.
wheel events carry deltaY and deltaMode values that vary by hardware. Chrome converts each scroll notch to 100 physical pixels, then divides by devicePixelRatio — so on a scaled display (e.g. 110% = DPR 1.1), a notched wheel produces fractional deltas like 90.909. This means fractional deltas don't necessarily mean trackpad. True trackpad deltas are small fractional values (sub-pixel, like 0.5 or 1.33), while scaled wheel deltas are large and consistent. Free-spinning wheels produce rapid bursts of events (<20ms apart).
Standard mice have 3 buttons (left, right, middle — indices 0, 1, 2). Gaming mice often have side buttons (forward/back, DPI switch, etc) which register as button indices 3, 4, and beyond. Any button above 2 strongly indicates gaming hardware. However, buttons 3 and 4 (back/forward) are typically intercepted by the browser for page navigation before JavaScript sees them — so detecting them requires preventDefault() in a controlled area, making passive detection difficult in the wild.
The Gamepad API exposes connected controllers, including manufacturer and product name in the id string. However, browsers require the user to press a button on the controller before it becomes visible — navigator.getGamepads() returns nulls until then. This is a deliberate privacy gate, so passive detection is impossible. A site would need to prompt the user to interact with their controller first.
WebGL exposes the exact GPU model via the WEBGL_debug_renderer_info extension. This immediately classifies your GPU tier — a "GeForce RTX 4090" is a gamer, an "Intel UHD 630" is not. The unmasked renderer string is one of the most precise hardware identifiers available to the browser.
WebGL exposes ~30 capability parameters (MAX_TEXTURE_SIZE, MAX_VIEWPORT_DIMS, etc.) that vary by GPU. High-end gaming GPUs report larger limits. The exact combination of values is highly unique — even GPUs from the same family can differ by driver version.
Drawing text and shapes on a <canvas> and hashing the pixel output produces a fingerprint unique to your GPU, driver, and OS. Different GPUs render sub-pixel antialiasing and color blending slightly differently, even for identical draw commands. This is one of the most widely deployed fingerprinting techniques.
CSS media queries can detect HDR support (dynamic-range: high) and wide color gamut (color-gamut: p3). Gaming monitors increasingly support HDR and wide color. Standard office monitors are SDR with sRGB only.
screen.width, screen.height, and devicePixelRatio reveal your display. Certain combos are gaming-specific: 2560x1440, 3440x1440 ultrawide, 1920x1080 at high refresh. screen.isExtended is a boolean (no permission needed) that reveals if you have multiple monitors — gamers disproportionately use multi-monitor setups. The rAF vs mousemove rate mismatch from tests 1-2 can also confirm multi-monitor if the rates differ.
Standard USB HID protocol limits keyboards to 6 simultaneous keys (6KRO). Gaming mechanical keyboards use custom drivers or NKRO mode to report unlimited simultaneous keypresses. If we detect 7+ keys held at once, it's almost certainly a mechanical/gaming keyboard with NKRO enabled. Press and hold as many keys as you can at once.
hardwareConcurrency reports logical CPU cores (gaming PCs: 12-32, office: 4-8). deviceMemory reports RAM in GB (capped at 8 in Chrome, but still discriminating). High values suggest a gaming or workstation build.
Game launchers install distinctive fonts: Steam installs "Motiva Sans", Epic Games installs "Brutal Type", GOG Galaxy installs fonts too. By measuring text width with each font vs a fallback, we can detect which gaming platforms you have installed — without any filesystem access.
The Battery API reveals whether you're on a desktop (no battery) or laptop. Desktops are more common among gamers. If a battery is present, its charge level and charging status add entropy to the fingerprint.
WebGPU is the successor to WebGL and exposes GPU info through requestAdapter(). The adapter's info object can reveal vendor, architecture, device, and description — sometimes more specific than WebGL's renderer string. Not all browsers support this yet.
The NetworkInformation API exposes connection type, effective type, downlink speed (Mbps), RTT (ms), and whether data-saver is on. Wired ethernet with high downlink and low RTT is more common for desktop gaming setups. The exact values are fingerprint entropy — they vary by ISP, router, and connection quality.
navigator.mediaDevices.enumerateDevices() lists audio/video inputs and outputs. Without microphone permission, labels are hidden — but the count of devices is still exposed. Multiple audio outputs (e.g. headset + speakers + monitor audio) or a USB microphone are common in gaming setups. The number and types of devices add fingerprint entropy.
navigator.userAgentData.getHighEntropyValues() can reveal OS, architecture (x86 vs ARM), bitness (32 vs 64), platform version, and whether it's mobile. Gaming is overwhelmingly x86-64 Windows. This API requires a promise and may be gated in some browsers.
navigator.maxTouchPoints reports the maximum number of simultaneous touch points supported. Desktop gaming machines typically report 0 (no touchscreen). Laptops with touchscreens report 5-10. Phones report 5+. A value of 0 on Windows is a strong desktop signal.