<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[hardwaretest]]></title><description><![CDATA[hardwaretest]]></description><link>https://hardwaretest.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Thu, 08 Oct 2026 11:33:07 GMT</lastBuildDate><atom:link href="https://hardwaretest.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[What a Browser Can Really Tell You About a Game Controller]]></title><description><![CDATA[A browser can show whether a controller button responds, how far an analog stick rests from center, and whether gamepad updates arrive consistently. It cannot inspect raw USB packets, certify total in]]></description><link>https://hardwaretest.hashnode.dev/what-a-browser-can-really-tell-you-about-a-game-controller</link><guid isPermaLink="true">https://hardwaretest.hashnode.dev/what-a-browser-can-really-tell-you-about-a-game-controller</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[gamepad]]></category><category><![CDATA[Web API]]></category><dc:creator><![CDATA[jack]]></dc:creator><pubDate>Sun, 27 Sep 2026 02:35:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6959d04deb83f06144329bb7/cea52412-8cb8-4c50-ae35-efe4bbc7df74.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A browser can show whether a controller button responds, how far an analog stick rests from center, and whether gamepad updates arrive consistently. It cannot inspect raw USB packets, certify total input latency, or repair worn hardware. That distinction shaped the diagnostic workflow we built with the Gamepad API.</p>
<p><em>Disclosure: I work on ControllerTest, the browser-based diagnostic linked at the end of this article.</em></p>
<h2>Start with the browser's view of the controller</h2>
<p>The first connection can look broken. You connect a gamepad, call navigator.getGamepads(), and get an empty array. After you press a physical button, the device appears. The <a href="https://www.w3.org/TR/gamepad/">W3C Gamepad specification</a> describes this user-gesture gate, so the interface should tell users to press a button instead of leaving them at a loading indicator.</p>
<p>Once a device is exposed, each snapshot includes buttons, axes, an identifier, a mapping string, and a timestamp. A basic read loop looks like this:</p>
<pre><code class="language-js">function readGamepads() {
  const pads = Array.from(navigator.getGamepads?.() ?? [])
    .filter(Boolean);

  for (const pad of pads) {
    const leftStick = {
      x: pad.axes[0] ?? 0,
      y: pad.axes[1] ?? 0,
    };
    const pressedButtons = pad.buttons
      .map((button, index) =&gt; ({ index, value: button.value }))
      .filter((button) =&gt; button.value &gt; 0.35);

    // Update your UI with the latest browser-visible state.
    renderController(pad, leftStick, pressedButtons);
  }

  requestAnimationFrame(readGamepads);
}

requestAnimationFrame(readGamepads);
</code></pre>
<p>The animation loop determines how often the page <em>looks</em> at the controller. It does not determine how often the controller, operating system, or browser produces fresh input. A page rendering at 144 frames per second has not turned a 125 Hz controller into a 144 Hz device.</p>
<h2>Measure stick drift over time</h2>
<p>A stick at rest rarely reports exactly (0, 0) on every read. One snapshot cannot distinguish sensor noise from a persistent offset. Ask the user to leave the controller untouched, then collect a rest sample for several seconds. For each reading, calculate its distance from center:</p>
<pre><code class="language-js">function stickOffset(x, y) {
  return Math.hypot(x, y);
}

const offset = stickOffset(
  pad.axes[0] ?? 0,
  pad.axes[1] ?? 0
);
samples.push(offset);
</code></pre>
<p>The average describes the usual resting position; the maximum catches brief spikes that may still move a camera in a game. A suggested game deadzone can start slightly above the observed maximum, but it is only a starting point. Increasing the deadzone may hide unwanted movement while making fine aim less responsive. It does not recalibrate or repair the stick.</p>
<p>Repeat a borderline result with the controller still. If the problem appears in one game only, inspect that game's deadzone and input profile before assuming the hardware has failed.</p>
<h2>Treat polling rate as an estimate</h2>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/API/Gamepad/timestamp">Gamepad.timestamp</a> indicates when the browser last updated a gamepad's data. Differences between successive useful timestamps provide browser-visible intervals:</p>
<pre><code class="language-js">const intervalMs = pad.timestamp - previousTimestamp;
const estimatedHz = 1000 / intervalMs;
</code></pre>
<p>Do not use a single interval as the result. Gather many intervals while the user moves a stick continuously, discard obvious outliers, and show the sample count beside the average and jitter. If timestamps are unhelpful, timing changes in button and axis values can provide a fallback, with the same caveat: it measures changes visible to the page.</p>
<p>A browser sits downstream from firmware, the connection, the operating system input stack, and its own scheduler. Foreground-tab behavior and display refresh can influence what JavaScript observes. The resulting number is useful for comparing USB, Bluetooth, and 2.4 GHz on the <em>same computer and browser</em>. It is not a raw USB polling measurement or a measure of total input lag.</p>
<h2>Check the outer stick range too</h2>
<p>A stick may return cleanly to center yet fail to reach part of its outer range. Ask the user to rotate it around the full gate, then turn each x/y sample into a radius and angle:</p>
<pre><code class="language-js">const radius = Math.hypot(x, y);
const angle = Math.atan2(y, x);
</code></pre>
<p>Divide the circle into angular bins and report how many bins received an outer-range sample. Show coverage together with radius variation and the visible trace. Different physical gates, firmware normalization, browser mappings, and the user's motion all affect the shape, so one percentage should not become a hardware verdict.</p>
<h2>Detect mapping and haptics instead of assuming them</h2>
<p>A standard mapping makes common button and axis positions convenient, but generic HID devices, adapters, Joy-Cons, and remapping layers may expose a different order. Display raw indices and live values when the mapping is uncertain. The <a href="https://developer.mozilla.org/en-US/docs/Web/API/Gamepad">MDN Gamepad reference</a> also notes that support varies by device and platform.</p>
<p>The same caution applies to vibration. Check whether the current browser exposes a compatible actuator, and offer a short pulse triggered by the user. If no actuator is exposed, say “vibration is unavailable in this browser and device combination.” That does not prove the controller's motors are broken.</p>
<h2>Make the result repeatable</h2>
<p>A useful diagnostic separates direct readings from timed samples, estimates, and capability checks. It tells users what to do, what the browser actually observed, and what deserves another test. A local JSON or CSV export can preserve the results for repair notes or before-and-after comparisons without uploading controller readings.</p>
<p>You can <a href="https://controllertestonline.com/?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=gamepad_api">try this workflow in ControllerTest</a>. It combines button and trigger checks, an eight-second drift sample, deadzone guidance, circularity, a browser-visible polling estimate, vibration detection, and local report exports. ControllerTest is our project, so we may benefit from visits to that link.</p>
]]></content:encoded></item><item><title><![CDATA[I Tried "Vibe Coding" a Hardware Test Site: AI is Powerful, But It's Not Magic (Yet) 🛠️]]></title><description><![CDATA[👋 The Backstory
I want to share a recent "experimental project" of mine: HardwareTest.org.
The motivation was simple: I bought some new peripherals and wanted to test them. But I was fed up with the existing tools—screens full of ads, outdated UIs, ...]]></description><link>https://hardwaretest.hashnode.dev/i-tried-vibe-coding-a-hardware-test-site-ai-is-powerful-but-its-not-magic-yet</link><guid isPermaLink="true">https://hardwaretest.hashnode.dev/i-tried-vibe-coding-a-hardware-test-site-ai-is-powerful-but-its-not-magic-yet</guid><dc:creator><![CDATA[jack]]></dc:creator><pubDate>Tue, 09 Dec 2025 01:35:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767494299685/cc455f32-0434-46d2-94a8-57ae1c138fe0.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>👋 The Backstory
I want to share a recent "experimental project" of mine: HardwareTest.org.</p>
<p>The motivation was simple: I bought some new peripherals and wanted to test them. But I was fed up with the existing tools—screens full of ads, outdated UIs, or sketchy .exe files that I didn't want to download.</p>
<p>As a self-described "average developer," I recently got brainwashed by the concept of "Vibe Coding" (coding by natural language/AI intuition). I thought, "AI is so strong now. I'll just write the prompts, let the AI write the code, and I'll be done in minutes, right?"</p>
<p>Spoiler Alert: I was too naive. 😂
While AI absolutely lowered the barrier to entry and boosted my speed by 10x, taking a tool from "it works" to "it feels good to use" was full of hidden traps.</p>
<p>🚧 The Real Challenges
Here is a breakdown of the actual struggles I faced while pair-programming with AI:</p>
<ol>
<li><p>The Tooling Chaos
My workflow was a bit of a mess. I started with Antigravity (it designed the initial UI), but ran out of credits. I switched to Codex to finish the logic. For the blog content, I used Gemini, but integrating that content back into the project via Codex resulted in a formatting nightmare. It was a lot of back-and-forth "fixing" what the AI broke.</p>
</li>
<li><p>Browser Limitations vs. Physics (The Keyboard Test)
I thought testing Keyboard Polling Rate would be simple: just tell the AI to "write an event listener."</p>
</li>
</ol>
<p>The Reality: I discovered that the browser's Event Loop often can't even keep up with a 1000Hz gaming keyboard. The raw data coming out was jittery and unusable. The Fix: I was forced into dozens of rounds of conversation with the AI. We had to optimize the algorithm, add debounce logic, and implement sliding averages just to get a relatively accurate "Real-time Hz Dashboard" on the web.</p>
<ol start="3">
<li><p>The Devil is in the Details (The Mouse Test)
I assumed a mouse test was just listening for onClick. The Reality: To properly test for Double Click issues (a gamer's nightmare) and Scroll Wheel rollback, you need very precise counting logic. Also, the AI kept confusing "Middle Click" (pressing the wheel) with "Scrolling" (spinning the wheel). It took a lot of human intervention to separate those events cleanly.</p>
</li>
<li><p>The SEO Battle
Writing the code was just step one. To get this English-language site indexed by Google, I spent ages wrestling with Schema, FAQ, and JSON-LD. The Insight: AI writes syntactically correct code, but often logically nonsensical SEO tags. This led to Google Search Console errors that I had to manually debug and patch.</p>
</li>
</ol>
<p>✨ The Result
Despite the process being more twisted than I expected, I'm actually really proud of the final result. It is a pure static, ad-free, dark-mode online hardware diagnostic suite.</p>
<p>👉 Check it out here: www.hardwaretest.org</p>
<p>Current Features:</p>
<p>⌨️ Keyboard Test: Visualizer with a real-time Hz polling rate dashboard (and Ghosting/NKRO support).</p>
<p>🖱️ Mouse Test: Left/Right/Middle buttons + Scroll Wheel + Double Click detection.</p>
<p>🖥️ Dead Pixel &amp; Fixer: Standard color cycle test, plus a "High-Frequency Noise Repair" feature built with Canvas.</p>
<p>🎧 Audio Test: Left/Right channel separation + Logarithmic Sweep.</p>
<p>This "Vibe Coding" experience taught me a valuable lesson: AI is an incredibly fast junior developer. It can speed up production by 1000%, but it cannot yet replace the human eye for product details, edge cases, and user experience.</p>
<p>🙏 Feedback Welcome! The site just went live, so there are definitely bugs and rough edges. If you have a moment to try it out, I’d love to hear your feedback in the comments!</p>
]]></content:encoded></item></channel></rss>