Color Match Reflex Game — Fast-Paced Browser Mini-Game with High Score

Color Match Reflex Game · Games · Plain HTML, CSS & JS · Live preview

What's included

Features

Fully playable game loop — start, score, lose lives, and game-over states all genuinely wired together
Difficulty scales in real time: the per-round time limit shrinks as the score increases, with a hard floor
Shared handleMiss() failure path for both wrong clicks and timeouts prevents duplicated logic drift
Stale-timer bug avoided by clearing the round timer on every possible round-ending event
Persistent high score stored in localStorage, surviving page reloads
Deliberate word/color mismatch (a Stroop-effect twist) makes the game genuinely test reflexes, not just reading
Shuffled 6-of-N color subset per round keeps swatch positions unpredictable
Wrong-answer shake animation gives immediate tactile feedback on a miss

About this UI Snippet

Color Match Reflex Game — A Complete Playable Mini-Game

Screenshot of the Color Match Reflex Game snippet rendered live

This is a small but fully playable arcade-style reflex game: a color name appears (deliberately rendered in a *different*, mismatched color to add a Stroop-effect twist), and the player has a shrinking time window to tap the swatch that actually matches the named color, not the color the word is printed in.

A genuinely difficulty-scaling round timer

Each round's time limit isn't fixed — Math.max(1200, 2600 - score * 40) starts new players at 2.6 seconds per round and shrinks that window by 40ms per point scored, clamped to a 1.2-second floor. This is what makes the game actually get harder as a player improves, rather than staying at a constant, eventually-trivial difficulty — a genuine progression curve rather than a static challenge.

Lives, misses, and timeouts share one failure path

Whether a player clicks the wrong swatch or simply runs out of time, both routes call the same handleMiss() function, which decrements lives, updates the dot indicator (filled circles for remaining lives, hollow for lost ones), and either starts a new round or ends the game — keeping the failure logic in one place rather than duplicating "lose a life" behavior across two separate code paths that could drift out of sync.

Persisting a real high score across sessions

The best score isn't just held in a JS variable — it's read from and written to localStorage (colorMatchBest), so a player's personal best survives a page reload or returning to the game later, which is what makes "Best" a meaningful stat rather than a number that resets every time the page loads.

Cleaning up stale timers correctly

Every place a round ends — a correct pick, a wrong pick, or a timeout — calls clearTimeout(roundTimer) before doing anything else. Without this, a player who answers correctly right as the old timer is about to fire would trigger a duplicate, stale "miss" for a round that's already been won, silently costing them a life they didn't actually lose — a subtle bug this snippet deliberately avoids by always clearing the previous round's timer before starting the next one.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Ask an AI assistant to explain why sharing one handleMiss() function between wrong-click and timeout failure paths avoids subtle bugs, and to walk through the exact race condition that clearTimeout(roundTimer) is protecting against on a correct answer. It's also worth asking for a version with combo multipliers for consecutive correct answers, or a version that swaps the color-word mechanic for a shape-matching or audio-matching variant using the same round/timer/lives architecture.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a fast-paced color-matching reflex game in HTML, CSS and vanilla JavaScript — no external libraries.

Requirements:
- Display a color name as text, deliberately rendered in a visually different color than the one it names, alongside a grid of colored swatch buttons including the correct match among several distractors.
- The player must click the swatch matching the color NAME (not the color the text is visually rendered in) before a per-round timer expires.
- Track score (increments on a correct match) and lives (3 total, decrementing on either a wrong click or a round timing out) with both values displayed live in a HUD.
- The per-round time limit must genuinely decrease as the score increases (with a reasonable minimum floor), so the game gets objectively harder as the player improves — not stay at a fixed difficulty.
- Ensure that answering correctly reliably cancels that round's pending timeout, so a last-moment correct click never also triggers a stale "miss" from the same round's expiring timer.
- Persist the player's best score across page reloads using localStorage, and show both the current score and the persisted best score in the HUD.
- Show a game-over state when lives reach zero, with a "Play again" action that fully resets score, lives, and starts a new round.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Source Code

<div class="demo">
  <div class="game" id="game">
    <div class="hud">
      <div class="stat"><span class="stat-label">Score</span><span class="stat-val" id="score">0</span></div>
      <div class="stat"><span class="stat-label">Best</span><span class="stat-val" id="best">0</span></div>
      <div class="stat"><span class="stat-label">Lives</span><span class="stat-val" id="lives">●●●</span></div>
    </div>

    <div class="target-wrap">
      <span class="target-label">Tap the swatch matching:</span>
      <div class="target-word" id="targetWord">RED</div>
    </div>

    <div class="swatches" id="swatches"></div>

    <p class="msg" id="msg">Click Start to play</p>
    <button class="start-btn" id="startBtn">Start</button>
  </div>
</div>

Step by step

How to Use

  1. 1
    Click Start to beginA target color name appears; tap the swatch matching that name (not the color the word is printed in) before time runs out.
  2. 2
    Adjust the color paletteAdd or remove entries in the colors array in the JS panel — each needs a name and a matching hex value.
  3. 3
    Tune the difficulty curveChange the 2600 starting value or the 40-per-point scaling in the timeLimit calculation to make rounds harder or easier over time.
  4. 4
    Change the starting lives countUpdate the lives = 3 assignment in startGame() and the matching dot-rendering logic in handleMiss().
  5. 5
    Reset the stored high scoreClear the colorMatchBest key from localStorage in your browser devtools to reset the persisted best score during testing.

Real-world uses

Common Use Cases

Standalone Browser Mini-Game
A complete, self-contained arcade game for a games section, loading screen, or 404 page easter egg.
DEMO
JavaScript Game-Loop Teaching Example
A clean reference for structuring round-based game state (score, lives, timers) without a game engine.
MARKETING
Interactive Marketing Widget
Embed as a playful interactive element to increase time-on-page for a product or campaign site.
UX
Reflex/Attention Testing Tool
Repurpose the core mechanic for a simple attention or reaction-speed measurement tool.
Related: Canvas Conway
See the Canvas Conway for a related games pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

It's a deliberate Stroop-effect twist — reading the word and matching its printed color would be a different, easier task. Forcing the player to read the word's meaning while ignoring its visual color makes the game genuinely test quick color-word association rather than simple color matching.

The per-round time limit shrinks with score via Math.max(1200, 2600 - score * 40) — starting near 2.6 seconds and dropping by 40ms per correct answer, down to a 1.2-second floor, so later rounds genuinely demand faster reflexes.

The click handler calls clearTimeout(roundTimer) immediately, canceling the pending timeout before it can fire — this prevents the classic bug where a just-in-time correct answer would still trigger a stale miss from the expiring timer.

Yes — it's persisted in the browser's localStorage under the key colorMatchBest, so a player's best score survives page reloads and future visits, not just the current session.

Yes — update the lives = 3 assignment in startGame(), and adjust the dot-rendering logic in handleMiss() (which currently assumes a 3-life maximum) to match your new starting value.

Yes — the swatches are plain buttons responding to click events, which fire correctly for both mouse clicks and touch taps with no special touch-event handling required.