<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Waqar ashraf</title>
    <description>The latest articles on DEV Community by Waqar ashraf (@hitthebuttonio).</description>
    <link>https://dev.to/hitthebuttonio</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4101088%2Fc4fa89ad-55d2-4048-9bf8-ab3563e86f68.webp</url>
      <title>DEV Community: Waqar ashraf</title>
      <link>https://dev.to/hitthebuttonio</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hitthebuttonio"/>
    <language>en</language>
    <item>
      <title>Building a Fair Reaction Time Test in the Browser</title>
      <dc:creator>Waqar ashraf</dc:creator>
      <pubDate>Sun, 30 Aug 2026 07:26:45 +0000</pubDate>
      <link>https://dev.to/hitthebuttonio/building-a-fair-reaction-time-test-in-the-browser-2kjm</link>
      <guid>https://dev.to/hitthebuttonio/building-a-fair-reaction-time-test-in-the-browser-2kjm</guid>
      <description>&lt;p&gt;A reaction time test is one of those projects that looks tiny until you try to make the result fair. The basic version needs only a coloured target, a random delay, and a timer. A useful version has to handle false starts, input differences, slow frames, touch screens, repeated attempts, and clear feedback.&lt;/p&gt;

&lt;p&gt;I built a browser-based reaction test as part of HitTheButton.io. This post explains the engineering choices behind it and the testing method I used. It is not a medical or diagnostic tool. It is a small web game designed for repeatable practice and honest feedback.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with a simple state machine
&lt;/h2&gt;

&lt;p&gt;The cleanest way to avoid confusing behaviour is to model the round as a few explicit states:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Idle&lt;/strong&gt; — the player has not started.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Waiting&lt;/strong&gt; — the timer is hidden and the game is waiting for a random delay.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ready&lt;/strong&gt; — the target has changed colour and input is now valid.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result&lt;/strong&gt; — the reaction time is displayed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;False start&lt;/strong&gt; — the player acted before the signal.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This structure keeps the click handler small. It does not need to guess what the player intended. It checks the current state and performs the matching action.&lt;/p&gt;

&lt;p&gt;A single boolean such as "started" is usually not enough. It cannot clearly distinguish waiting from ready, and that is where early clicks get counted by mistake.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use a random waiting period
&lt;/h2&gt;

&lt;p&gt;If the signal always appears after exactly two seconds, players learn the rhythm instead of reacting to the visual change. The game therefore chooses a random delay for each attempt.&lt;/p&gt;

&lt;p&gt;The range should feel long enough to prevent prediction but short enough to avoid frustration. It is also useful to keep that range visible in the rules so players understand that the pause is intentional.&lt;/p&gt;

&lt;p&gt;The pending timeout must be cancelled whenever the round resets. Otherwise, an old timeout may fire during a later round and turn the target green at the wrong moment. I keep the timeout identifier with the round state and clear it before starting every new attempt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measure elapsed time, not frames
&lt;/h2&gt;

&lt;p&gt;For short intervals, the browser's high-resolution performance clock is a better fit than counting animation frames. Frames are for drawing; they are not a dependable clock. A busy device may skip frames, and background work can delay visual updates.&lt;/p&gt;

&lt;p&gt;When the signal appears, the game stores a start timestamp. When the player responds, it subtracts that value from a fresh timestamp. The displayed number is rounded for readability, but the internal measurement stays precise until the final step.&lt;/p&gt;

&lt;p&gt;This separation also makes the interface easier to change. The timer logic does not depend on how often the screen is redrawn.&lt;/p&gt;

&lt;h2&gt;
  
  
  Treat false starts as part of the test
&lt;/h2&gt;

&lt;p&gt;An early press should not produce a fast score. It should stop the round and explain what happened. This protects the result and teaches the player to wait for the actual signal.&lt;/p&gt;

&lt;p&gt;The message should be neutral. "Too soon — try again" is more useful than a dramatic penalty. The next attempt should be available immediately, especially on mobile where accidental taps are common.&lt;/p&gt;

&lt;p&gt;A false start also needs to cancel the pending ready signal. Without that cleanup, the button may turn green after the failure message appears.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep input paths consistent
&lt;/h2&gt;

&lt;p&gt;A browser game may receive input from a mouse, touchscreen, keyboard, or assistive technology. The scoring logic should live in one function, while different event handlers call that function.&lt;/p&gt;

&lt;p&gt;That reduces the chance of one input method getting different rules. It also helps prevent double counting when a touch event is followed by a synthetic click.&lt;/p&gt;

&lt;p&gt;For keyboard play, Space and Enter are sensible controls. During an active round, the game prevents Space from scrolling the page. Outside the game, normal browser behaviour remains available.&lt;/p&gt;

&lt;p&gt;Pointer events are useful for mouse, pen, and touch support, but the main control should still be a real button. Native buttons provide focus behaviour and keyboard accessibility that a generic container does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design for repeatable attempts
&lt;/h2&gt;

&lt;p&gt;One score can be affected by distraction, device load, anticipation, or a slightly late visual frame. A useful game makes repeated attempts easy and shows the best score without implying that a single number defines the player.&lt;/p&gt;

&lt;p&gt;I store a personal best in local storage. It stays on the current device and does not require an account. If storage is unavailable, the game still works; it simply skips persistence.&lt;/p&gt;

&lt;p&gt;A short history can also help players notice consistency. I prefer a small list of recent attempts instead of a complicated dashboard. The game remains quick to understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explain measurement limits
&lt;/h2&gt;

&lt;p&gt;Browser reaction tests include more than human response time. The result can also be affected by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;display refresh and pixel response;&lt;/li&gt;
&lt;li&gt;mouse, keyboard, or touchscreen latency;&lt;/li&gt;
&lt;li&gt;browser scheduling;&lt;/li&gt;
&lt;li&gt;background applications;&lt;/li&gt;
&lt;li&gt;power-saving modes;&lt;/li&gt;
&lt;li&gt;the way a person holds or presses the device.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For that reason, comparisons are more meaningful when the same person uses the same device and input method under similar conditions.&lt;/p&gt;

&lt;p&gt;Age-based comparisons also need careful wording. They are descriptive references, not diagnoses or fixed targets. I wrote a separate, plain-language overview of &lt;a href="https://hitthebutton.io/average-reaction-time-by-age-for-kids/" rel="noopener noreferrer"&gt;average reaction time by age for kids&lt;/a&gt; that explains these limitations and encourages consistent test conditions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make mobile layout intentional
&lt;/h2&gt;

&lt;p&gt;A target that works with a mouse may be awkward for a thumb. On narrow screens, the active area needs enough height and the button needs generous spacing from browser controls.&lt;/p&gt;

&lt;p&gt;The score should not jump around when its value changes. Stable dimensions prevent layout shifts during the moment when the player is trying to react.&lt;/p&gt;

&lt;p&gt;I also avoid heavy animation before the signal. Decorative motion can become an unintended cue or distraction. The ready state should have one strong visual change with sufficient contrast.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the failure cases
&lt;/h2&gt;

&lt;p&gt;The happy path is easy. The useful tests are the awkward ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tap twice quickly;&lt;/li&gt;
&lt;li&gt;press before the signal;&lt;/li&gt;
&lt;li&gt;restart while waiting;&lt;/li&gt;
&lt;li&gt;switch tabs during a round;&lt;/li&gt;
&lt;li&gt;rotate a phone;&lt;/li&gt;
&lt;li&gt;press Space while the page can scroll;&lt;/li&gt;
&lt;li&gt;start a new round immediately after a result;&lt;/li&gt;
&lt;li&gt;disable local storage;&lt;/li&gt;
&lt;li&gt;use keyboard-only navigation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I test those cases manually on desktop and mobile widths. I also inspect the state transitions during development. If the game ever reaches a state that the interface does not represent, the model needs simplifying.&lt;/p&gt;

&lt;p&gt;Tab switching deserves special attention. A hidden tab can delay timers. When the player returns, the game should either calculate from timestamps honestly or reset the round with a clear message. Quietly continuing from an unreliable state creates confusing scores.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the result honest
&lt;/h2&gt;

&lt;p&gt;A fair reaction game does not need a complex framework. It needs a clear state machine, a monotonic clock, consistent input handling, careful reset logic, and honest explanations.&lt;/p&gt;

&lt;p&gt;The most important design choice is restraint. Do not claim scientific precision that the browser cannot provide. Do not turn a false start into a score. Do not hide the role of device latency. Give players a responsive tool, explain what it measures, and make the next attempt easy.&lt;/p&gt;

&lt;p&gt;That approach produces a small game that feels dependable, which matters far more than making the interface look technically impressive.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
