Methodology & Math

Transparent technical documentation for StickDriftLab's diagnostic engine

W3C Gamepad API Zero Dependencies Client-Side Only Open Source

Executive Summary

StickDriftLab is a browser-based diagnostic suite that measures analog stick drift, circularity error, deadzone thresholds, and polling rates for game controllers. All computation runs entirely in your browser using the W3C Gamepad API and HTML5 Canvas. No data is transmitted to any server; no cookies, no tracking, no external dependencies.

This document describes the mathematical foundations, implementation details, and calibration procedures so that engineers, reviewers, and power users can verify the results independently.

1. Data Source: W3C Gamepad API

The navigator.getGamepads() method returns a snapshot of connected gamepads. Each Gamepad object exposes:

Sampling Strategy: The main loop uses requestAnimationFrame (~60 Hz) to poll the API. For polling-rate measurement, frame timestamps are recorded and a rolling window of 30 frames computes actualHz = (frames - 1) / (lastTimestamp - firstTimestamp) * 1000.

2. Stick Drift Detection

2.1 Rest Position Magnitude

When the user is not touching the stick, the axes should report (0, 0). Manufacturing tolerances and wear cause a non-zero rest vector. The drift magnitude is the Euclidean distance from origin:

magnitude = sqrt(x² + y²)

Where x = axes[0] (left stick X) and y = axes[1] (left stick Y).

2.2 Threshold Classification

A 0.05 (5%) magnitude threshold separates "healthy" from "drifting":

if (magnitude > 0.05) → DRIFT DETECTED
else → HEALTHY

This 5% threshold is derived from industry-standard deadzone recommendations (most games default to 5-10% inner deadzone). A stick drifting beyond 5% at rest will cause visible in-game movement without user input.

2.3 Grade Bands

GradeMagnitude RangeInterpretationAction
A (Clean)0.000 – 0.025No measurable driftNone needed
B (Minor)0.025 – 0.065Compensable via deadzoneIn-game deadzone 5-10%
C (Heavy)> 0.065Hardware wear likelyContact cleaner / replacement

3. Circularity Error Benchmark

3.1 Theory

When a user rotates an analog stick through 360°, the ideal path is a perfect unit circle (radius = 1.0). Real sticks deviate due to:

3.2 Angular Binning Algorithm

The engine records the maximum radius observed at each 1° angular bin during a user-initiated 360° rotation:


// For each sampled point (x, y):
magnitude = sqrt(x² + y²)
if (magnitude >= 0.70) {  // ignore inner deadzone
  angle = atan2(y, x) * 180/PI  // [-180, 180]
  bin = (angle + 360) % 360      // [0, 359]
  bins[bin] = max(bins[bin] || 0, magnitude)
}

3.3 Error Metrics

Average Circularity Error: Mean absolute deviation from unit radius across all populated bins:


totalError = Σ |bins[i] - 1.0|  for all i where bins[i] !== null
avgError = (totalError / populatedBins) * 100  // percentage

Outer Coverage: Percentage of 360 bins with at least one sample:


coverage = (populatedBins / 360) * 100

3.4 Grade Interpretation

Avg ErrorGradeMeaning
< 2.5%AExcellent — near-perfect circle
2.5% – 6.5%BAcceptable — minor asymmetry
6.5% – 12%CWorn — deadzone adjustment helps
> 12%FSevere — hardware replacement

4. Deadzone Calibration Procedure

4.1 Measurement Phase

  1. Connect controller; wait for "Active" state.
  2. Release sticks completely; let them settle for 2 seconds.
  3. Read Rest Drift Offset from telemetry (magnitude × 100 = %).

4.2 Deadzone Calculation


// Rule: Set in-game deadzone to the NEXT whole percentage above measured drift
// Examples:
drift = 0.038 (3.8%)  → deadzone = 4%
drift = 0.052 (5.2%)  → deadzone = 6%
drift = 0.081 (8.1%)  → deadzone = 9%

4.3 Verification

Enable "Test Circularity" and rotate the stick 360°. The recorded trace should show a closed loop. If the circularity error exceeds 8%, consider cleaning or replacement.

5. Polling Rate Measurement

The browser's requestAnimationFrame callback fires at the display refresh rate (typically 60 Hz). To measure the controller's actual report rate:


let timestamps = []
function poll() {
  const now = performance.now()
  timestamps.push(now)
  if (timestamps.length > 30) timestamps.shift()
  if (timestamps.length >= 4) {
    const duration = timestamps[timestamps.length - 1] - timestamps[0]
    const hz = ((timestamps.length - 1) / duration) * 1000
  }
  requestAnimationFrame(poll)
}

The tier classification uses fixed bins: 125 Hz, 250 Hz, 500 Hz, 1000 Hz. Wired Xbox controllers typically report 1000 Hz; Bluetooth often drops to 250-500 Hz.

6. Trigger Gauge Accuracy

Analog triggers (LT/RT, L2/R2) report buttons[6].value and buttons[7].value in [0.0, 1.0]. The visual gauge maps this linearly to a 0-100% bar. No interpolation is applied — the raw API value is displayed.

7. Diagnostic Certificate (PNG Export)

The "Download PNG" button renders a certificate to an offscreen 800×460 canvas using the exact same drawing primitives as the live UI. The certificate includes:

Export uses canvas.toBlob('image/png') → URL.createObjectURL() for a reliable binary download. No server round-trip.

8. Privacy & Security (E-E-A-T)

PrincipleImplementation
No Network RequestsAll logic in app.js; no fetch/XMLHttpRequest
No Cookies / LocalStorageZero persistent storage
No Third-Party ScriptsSingle style.css + app.js only
User ConsentGamepad API requires explicit user gesture (press any button)
Verifiable OutputPNG contains raw telemetry; anyone can reproduce

9. Limitations & Known Issues

10. Reproducibility Checklist

To independently verify a certificate:

  1. Open https://StickDriftLab.com in a Chromium-based browser.
  2. Connect the same controller via USB.
  3. Press any button to activate.
  4. Wait 2 seconds at rest; note Rest Drift Offset.
  5. Run Circularity Test (360° rotation).
  6. Compare PNG telemetry values.

All source code is visible in DevTools (Sources → app.js). No minification, no obfuscation.