Build a Reaction Speed Game with JS

Build a browser reaction game using core JavaScript DOM and event patterns.

Introduction

30 Second Summary

You know that split-second feeling when a traffic light turns green and you lift your foot off the brake? That tiny gap between seeing something and reacting to it is your reaction speed.

In this project, you will build a browser game that measures your reaction speed in milliseconds. Along the way, you will learn the core JavaScript patterns that power every interactive website.

What You'll Build

A reaction speed game in a single HTML file where the player clicks a box the moment it turns green, with score tracking across rounds.

By the end of this project, you'll have:

  • A working reaction speed game that runs entirely in your browser with no setup or server required.
  • Hands-on experience with DOM manipulation, event listeners, and timing functions that control what happens on a webpage.
  • A complete understanding of state management using variables and conditional logic to track game progress.
  • Secret Mission: Add difficulty levels that change the timing window and visual feedback as you play.

Are there any prerequisites?

You only need a basic understanding of what HTML and CSS are. No prior JavaScript experience is needed. This project teaches you JavaScript from scratch.

Before We Start

You're about to build a reaction speed game that teaches you the fundamentals of JavaScript. By the end of this project, you'll understand how JavaScript controls what you see on a webpage, responds to your clicks, and keeps track of data.

Before writing any game code, you need a code editor and a project file. In this step, you'll install your editor, create a project folder, and verify everything works in the browser.

In this step, get ready to:
  • Install a code editor (Cursor).
  • Create your project folder and file.
  • Verify your setup works in the browser.
Install a code editor

A code editor is where you'll write all the code for your game. Cursor is a free, modern code editor that works on both macOS and Windows.

  • Open your web browser and go to cursor.com.
  • Click the download button for your operating system.
  • Once the download finishes, open the installer and follow the prompts to install Cursor.
  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Cursor and press Enter to open it.

Using VS Code instead?

If you prefer VS Code, download it from code.visualstudio.com. All the instructions in this project work identically in both editors.

Can't find Cursor after installing?

On macOS, check your Applications folder. On Windows, try searching for "Cursor" in the Start menu. If the install didn't complete, re-download from cursor.com.

Why can't I find Cursor on my computer?

Create your project folder and file

Every project needs a home folder to live in. You'll create a folder called reaction-game on your Desktop so it's easy to find.

  • In Cursor, click Terminal in the top menu bar.
  • Click New Terminal.

A terminal panel opens at the bottom of the editor. This is where you'll type commands to create folders and files.

  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder by running this command:
mkdir reaction-game
  • Confirm the folder exists by running:
ls

You should see reaction-game listed in the output.

Now open this folder in Cursor so the editor knows where your project lives.

  • Click File in the top menu bar.
  • Click Open Folder.
  • Navigate to your Desktop, select the reaction-game folder, and click Open.

What is a terminal?

A terminal is a text-based tool for giving your computer instructions. Instead of clicking through folders, you type short commands. It's built right into Cursor so you don't need to open a separate app.

Now create the file your game will live in.

  • In the Explorer sidebar on the left, right-click inside the empty area.
  • Select New File.
  • Type index.html and press Enter.

You should see index.html appear in the Explorer sidebar. The file also opens automatically in the editor area.

Don't see the Explorer sidebar?

Click View in the top menu bar, then click Explorer. The sidebar should appear on the left showing your project files.

Help me find the Explorer sidebar in Cursor.

Verify your setup

The final test is making sure your file shows up in a browser. HTML files can be opened directly by double-clicking them.

  • Click on index.html in the Explorer sidebar to open it in the editor (if it isn't already open).
  • Type hello into the file.
  • Save the file by pressing Cmd+S (macOS) or Ctrl+S (Windows).
  • Open the reaction-game folder on your Desktop using Finder (macOS) or File Explorer (Windows).
  • Double-click index.html to open it in your web browser.

You should see the word "hello" displayed on a blank white page. That confirms your editor and browser are connected to the same file.

Don't see "hello" in the browser?

Make sure you saved the file in Cursor (Cmd+S / Ctrl+S). Check that the file is named exactly index.html and not index.html.txt. Try refreshing the browser page.

Why isn't my HTML file showing content in the browser?

Your project is ready to go. Next up, you'll replace that "hello" with a real HTML structure for your reaction speed game.

Set Up Your Project

JavaScript runs inside a web browser. That means your development workflow has two key parts: a code editor where you write code, and a browser where you see the results.

Your editor and project file are ready to go. In this step, you'll confirm the connection between them by saving a change and checking that your browser picks it up.

In this step, get ready to:
  • Save test content to your HTML file.
  • Confirm your browser displays the file correctly.
Verify your setup

The quickest way to check your workflow is to put some text into your HTML file, save it, and see if the browser shows it.

  • In your code editor, switch to the index.html tab.
  • Delete anything already in the file and type the following:
hello
  • Save the file by pressing Cmd+S (macOS) or Ctrl+S (Windows).
  • Switch to your browser where index.html is already open.
  • Refresh the page by pressing Cmd+R (macOS) or F5 (Windows).

You should see the word hello displayed in the top-left corner of the page on a white background.

Why does this work?

A browser can read any .html file and display its contents. Right now there's no proper HTML structure in the file, so the browser just renders the raw text.

In the next step, you'll replace this with a real HTML document structure that gives the browser proper instructions.

Don't see hello in the browser?

  • Make sure you saved the file. Look for a dot or circle on the file tab in your editor, which means unsaved changes.
  • Confirm you refreshed the browser after saving. The browser does not update automatically.
  • Check that the browser is showing your index.html file. The address bar should show a path ending in reaction-game/index.html.

Help me figure out why my browser isn't showing my file changes.

✔️ Awesome, I've got everything!

Great. Make sure you've saved your file before moving on.

ⓧ I'd like to double check the full code

Your index.html file should contain exactly this:

hello

Your workflow is confirmed. You can edit code, save it, and see the results in your browser. Next up, you'll replace that placeholder text with a proper HTML structure for your reaction game.

Build the HTML Structure

Your project is set up and you've confirmed that your browser can display your HTML file. Now it's time to build the actual structure of your game.

HTML defines what elements appear on your page. Think of it as the skeleton that JavaScript and CSS will bring to life later. Right now you need a game box to click, a place for messages, and somewhere to show scores.

In this step, get ready to:
  • Build the HTML page skeleton with a head and body.
  • Create the game box, message area, and stats panel.
  • Add ID attributes so JavaScript can find and control each element.
Add the page skeleton

Every HTML page needs a standard structure that tells the browser what type of document it is, what language it uses, and where to find metadata like the page title.

  • Switch back to index.html in your code editor.
  • Select all of the existing text (hello) and delete it.
  • Replace it with this HTML page skeleton:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Reaction Speed Game</title>
</head>
<body>

</body>
</html>

What does this code do?

  • <!DOCTYPE html> tells the browser this is a modern HTML5 page.
  • <html lang="en"> wraps everything and declares the page language as English.
  • <head> contains metadata the browser needs (character encoding, viewport settings, page title). None of this is visible on the page itself.
  • <body> is where all the visible content goes. It's empty for now.
  • Save index.html by pressing Cmd+S (macOS) or Ctrl+S (Windows).
  • Refresh your browser. You should see a completely blank white page. The "hello" text is gone because you replaced it with a proper HTML structure that has an empty body.

Still seeing 'hello' on the page?

  • Make sure you saved the file. An unsaved file has a dot or circle on its tab in the editor.
  • Make sure you deleted all of the original text before pasting. Select everything with Cmd+A (macOS) or Ctrl+A (Windows), then paste the new code.
  • Confirm you're refreshing the same browser tab that has your file open.

Why is my page still showing the old content?

Create the game elements

Your page needs four main pieces: a heading, instructions for the player, the clickable game box, and a stats panel to show scores.

  • In index.html, find the empty space between the <body> and </body> tags.
  • Add the following game structure between those tags:
    <div class="game-container">
        <h1>Reaction Speed Game</h1>
        <p class="instructions">Click the box when it turns green. Don't click too early!</p>
        <div class="game-box" id="gameBox">
            <span class="message" id="message">Click to Start</span>
        </div>
        <div class="stats">
            <div class="stat-box">
                <div class="label">Last Time</div>
                <div class="value" id="lastTime">- ms</div>
            </div>
            <div class="stat-box">
                <div class="label">Best Time</div>
                <div class="value" id="bestTime">- ms</div>
            </div>
            <div class="stat-box">
                <div class="label">Round</div>
                <div class="value" id="round">0</div>
            </div>
        </div>
    </div>

What does this code do?

  • The game-container div wraps everything and will help center the game on the page later.
  • The game-box div is the large clickable area the player interacts with. The <span> inside it displays messages like "Click to Start" or "Wait for green...".
  • The stats section has three boxes that display the player's last reaction time, best time, and current round number.
  • Save index.html.
  • Refresh your browser. You should see raw unstyled text: the heading "Reaction Speed Game", the instruction text, "Click to Start", and the stat labels ("Last Time", "Best Time", "Round").

Don't see all the text?

  • Check that your new code sits between <body> and </body>. Content outside those tags will not display.
  • Make sure every opening tag (like <div>) has a matching closing tag (</div>). A missing closing tag can hide content below it.

Why isn't my HTML content showing up in the browser?

Understand the element IDs

Notice that some of the elements you just added have id attributes. These are how JavaScript will find and control specific elements on the page.

What is an ID attribute?

An id is a unique name you give to an HTML element. No two elements on the same page should share the same ID. JavaScript uses these names to grab specific elements and change them.

Your page has five IDs that JavaScript will need later:

  • gameBox on the clickable game area.
  • message on the text inside the game box.
  • lastTime, bestTime, and round on the stat values that update as you play.
  • Look at your code in the editor and find each id= attribute. You should count five of them.

✔️ Awesome, I've got everything!

Great. Double check you've saved your file and your browser shows all the raw text.

ⓧ I'd like to double check the full code

Here is the complete index.html file at this point. Make sure yours matches exactly.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Reaction Speed Game</title>
</head>
<body>
    <div class="game-container">
        <h1>Reaction Speed Game</h1>
        <p class="instructions">Click the box when it turns green. Don't click too early!</p>
        <div class="game-box" id="gameBox">
            <span class="message" id="message">Click to Start</span>
        </div>
        <div class="stats">
            <div class="stat-box">
                <div class="label">Last Time</div>
                <div class="value" id="lastTime">- ms</div>
            </div>
            <div class="stat-box">
                <div class="label">Best Time</div>
                <div class="value" id="bestTime">- ms</div>
            </div>
            <div class="stat-box">
                <div class="label">Round</div>
                <div class="value" id="round">0</div>
            </div>
        </div>
    </div>
</body>
</html>

Your game's skeleton is in place. Right now it looks like plain unstyled text, but all the pieces are there. Next, you'll add CSS styles to transform this raw structure into a polished, dark-themed game interface.

Style the Game with CSS

Your HTML structure is in place, but right now it looks like plain text on a white page. A game needs to feel like a game.

In this step, you'll add CSS styles to transform the page into a dark, polished interface with a large clickable game box and a clean stats panel underneath.

In this step, get ready to:
  • Add base styles for the page layout and game box.
  • Style the stats panel with flexbox.
  • Test the complete appearance in your browser.
Add base styles

CSS controls how your HTML elements look and are positioned on the page. You'll use a <style> tag in the <head> to keep all your styles in one place.

  • In index.html, find the closing </title> tag in the <head> section.
  • Add the following code between </title> and </head>:
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            background-color: #1a1a2e;
            color: #ffffff;
        }

        .game-container {
            text-align: center;
            width: 100%;
            max-width: 600px;
            padding: 20px;
        }

        h1 {
            font-size: 2rem;
            margin-bottom: 10px;
        }

        .instructions {
            font-size: 1.1rem;
            color: #aaaaaa;
            margin-bottom: 20px;
        }

        .game-box {
            width: 100%;
            height: 300px;
            border-radius: 16px;
            display: flex;
            justify-content: center;
            align-items: center;
            cursor: pointer;
            background-color: #16213e;
            transition: background-color 0.1s;
            margin-bottom: 20px;
            user-select: none;
        }

        .game-box .message {
            font-size: 1.5rem;
            font-weight: bold;
            padding: 20px;
        }
    </style>

What does this code do?

The * selector resets all default spacing so every element starts from zero. This prevents inconsistent spacing between browsers.

The body uses flexbox to center everything both horizontally and vertically. The dark background (#1a1a2e) gives the page a sleek game feel.

The .game-box is styled as a 300px tall clickable area with rounded corners. The cursor: pointer property makes the mouse change to a hand icon on hover, signaling that the box is clickable.

  • Save index.html and refresh your browser.
  • You should see a dark page with white text centered on the screen. The game box appears as a large dark rectangle with rounded corners and the text "Click to Start" in the center.

Still seeing a white page?

  • Make sure your <style> tag is inside the <head> section, not inside <body>.
  • Check that you have both an opening <style> and a closing </style> tag wrapping your CSS.
  • Confirm you saved the file before refreshing the browser.

Why isn't my CSS showing up in the browser?

Style the stats panel

The stats section needs to display three boxes side by side. Flexbox makes this layout straightforward.

  • In index.html, find the closing .game-box .message block (the one ending with padding: 20px;) inside your <style> tag.
  • Add the following CSS below it, before the closing </style> tag:
        .stats {
            display: flex;
            justify-content: space-around;
            gap: 20px;
        }

        .stat-box {
            background-color: #16213e;
            padding: 15px 25px;
            border-radius: 12px;
            flex: 1;
        }

        .stat-box .label {
            font-size: 0.85rem;
            color: #aaaaaa;
            margin-bottom: 5px;
        }

        .stat-box .value {
            font-size: 1.4rem;
            font-weight: bold;
        }

What does this code do?

The .stats container uses flexbox with justify-content: space-around to evenly distribute the three stat boxes in a row.

Each .stat-box has flex: 1 which makes all three boxes grow to fill the available space equally. The .label is styled as small gray text, while the .value is larger and bold so the numbers stand out.

  • Save index.html and refresh your browser.
  • You should see a dark page with a centered game box displaying "Click to Start" and three stat boxes below it showing "Last Time", "Best Time", and "Round".

Stats boxes not appearing side by side?

  • Check that the .stats CSS has display: flex. Without this, the boxes stack vertically.
  • Make sure the class name in your CSS (.stats) matches the class in your HTML (class="stats"). CSS class names are case-sensitive.

My stats panel layout is broken. How can I fix it?

✔️ Awesome, I've got everything!

Great. Double check you've saved your file before moving on.

ⓧ I'd like to double check the full code

Here is the complete index.html file at this point:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Reaction Speed Game</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            background-color: #1a1a2e;
            color: #ffffff;
        }

        .game-container {
            text-align: center;
            width: 100%;
            max-width: 600px;
            padding: 20px;
        }

        h1 {
            font-size: 2rem;
            margin-bottom: 10px;
        }

        .instructions {
            font-size: 1.1rem;
            color: #aaaaaa;
            margin-bottom: 20px;
        }

        .game-box {
            width: 100%;
            height: 300px;
            border-radius: 16px;
            display: flex;
            justify-content: center;
            align-items: center;
            cursor: pointer;
            background-color: #16213e;
            transition: background-color 0.1s;
            margin-bottom: 20px;
            user-select: none;
        }

        .game-box .message {
            font-size: 1.5rem;
            font-weight: bold;
            padding: 20px;
        }

        .stats {
            display: flex;
            justify-content: space-around;
            gap: 20px;
        }

        .stat-box {
            background-color: #16213e;
            padding: 15px 25px;
            border-radius: 12px;
            flex: 1;
        }

        .stat-box .label {
            font-size: 0.85rem;
            color: #aaaaaa;
            margin-bottom: 5px;
        }

        .stat-box .value {
            font-size: 1.4rem;
            font-weight: bold;
        }
    </style>
</head>
<body>
    <div class="game-container">
        <h1>Reaction Speed Game</h1>
        <p class="instructions">Click the box when it turns green. Don't click too early!</p>
        <div class="game-box" id="gameBox">
            <span class="message" id="message">Click to Start</span>
        </div>
        <div class="stats">
            <div class="stat-box">
                <div class="label">Last Time</div>
                <div class="value" id="lastTime">- ms</div>
            </div>
            <div class="stat-box">
                <div class="label">Best Time</div>
                <div class="value" id="bestTime">- ms</div>
            </div>
            <div class="stat-box">
                <div class="label">Round</div>
                <div class="value" id="round">0</div>
            </div>
        </div>
    </div>
</body>
</html>

Your game looks polished and ready to play. Next up, you'll bring it to life with JavaScript by making the box respond to clicks and change colors.

Add JavaScript Game Logic

Your game looks polished with its dark theme and centered layout. Now it's time to make it actually playable.

JavaScript is the language that makes webpages respond to your actions. In this step, you'll write the code that listens for clicks, tracks timing, and controls the flow of each round.

In this step, get ready to:
  • Select HTML elements so JavaScript can read and update them.
  • Add click handling that responds differently based on the game's current state.
  • Write a function that starts each round with a random delay.
Select your elements and set up state

Before JavaScript can change anything on the page, it needs a way to "find" the HTML elements you created earlier. document.getElementById() grabs a reference to an element by its id attribute, so you can read or update it later.

  • In index.html, scroll to the bottom and find the last </div> tag (the one that closes .game-container), just above </body>.
  • Add the following <script> block below that closing </div>:
    <script>
        // Grab references to the HTML elements we need to update
        var gameBox = document.getElementById('gameBox');
        var message = document.getElementById('message');
        var lastTimeDisplay = document.getElementById('lastTime');
        var bestTimeDisplay = document.getElementById('bestTime');
        var roundDisplay = document.getElementById('round');

        // Track the game's current state
        var state = 'idle';
        var timeoutId = null;
        var startTime = 0;
        var bestTime = Infinity;
        var roundCount = 0;
    </script>

What does this code do?

The first five lines use document.getElementById() to store references to each element you'll need to update during gameplay. Think of each variable as a "handle" you can pull to change that element.

The next five lines are your game's state variables. state tracks where the game is in its cycle (idle, waiting, or ready). timeoutId stores a reference to the timer so you can cancel it if the player clicks too early.

What is a state machine?

Your game uses a simple state machine pattern. The state variable always holds one of three values: 'idle', 'waiting', or 'ready'.

Each state determines what happens when the player clicks. This is the same pattern used in forms, animations, and multi-step wizards across all of web development.

  • Save index.html.

You won't see a visible change yet. The next chunk adds the click listener that makes the game respond to your actions.

Handle clicks with an event listener

An event listener tells the browser to run a function every time something happens (like a click). Your listener needs to check the current state and respond differently depending on where the game is in its cycle.

  • Inside your <script> tag, find the line var roundCount = 0; and add the following code below it:
        // Listen for clicks on the game box
        gameBox.addEventListener('click', function () {
            if (state === 'idle') {
                // Player wants to start a new round
                startRound();
            } else if (state === 'waiting') {
                // Player clicked too early!
                clearTimeout(timeoutId);
                gameBox.style.backgroundColor = '#e74c3c';
                message.textContent = 'Too early! Click to try again.';
                state = 'idle';
            } else if (state === 'ready') {
                // Player clicked at the right time - calculate reaction speed
                var reactionTime = Math.round(Date.now() - startTime);
            }
        });

What does this code do?

  • addEventListener('click', ...) runs the inner function every time the player clicks the game box.
  • If state === 'idle', the game hasn't started yet, so it calls startRound() (which you'll write next).
  • If state === 'waiting', the player jumped the gun. clearTimeout(timeoutId) cancels the pending green-light timer, and the box turns red with an error message.
  • If state === 'ready', the box is green and the player clicked. Date.now() - startTime calculates how many milliseconds elapsed. You'll display this result in the next step.
  • Save index.html.

Don't click the game box yet. The event listener calls startRound() which doesn't exist yet. You'll write it next.

Seeing an error in the browser console?

  • If you accidentally clicked the box, you'll see a "startRound is not defined" error in your browser console. That's expected. The function doesn't exist yet.
  • Check that your addEventListener code is placed below all your var declarations, not above them.

Help me debug my event listener code.

Write the startRound function

The startRound function kicks off a new round by turning the box red, then waiting a random amount of time before turning it green. The random delay is what makes the game challenging.

  • Below your addEventListener block (still inside the <script> tag), add this function:
        function startRound() {
            // Set state to waiting and show red box
            state = 'waiting';
            gameBox.style.backgroundColor = '#e74c3c';
            message.textContent = 'Wait for green...';

            // Random delay between 1000ms and 4000ms
            var delay = Math.floor(Math.random() * 3000) + 1000;

            // After the delay, turn box green and start timing
            timeoutId = setTimeout(function () {
                state = 'ready';
                gameBox.style.backgroundColor = '#2ecc71';
                message.textContent = 'Click NOW!';
                startTime = Date.now();
            }, delay);
        }

What does this code do?

  • The function immediately turns the box red and displays "Wait for green..." to build suspense.
  • Math.floor(Math.random() * 3000) + 1000 generates a random number between 1000 and 4000. This becomes the delay in milliseconds before the box turns green.
  • setTimeout() schedules a function to run after that delay. When it fires, the box turns green, state changes to 'ready', and startTime records the exact moment. This is the timestamp you'll subtract from later to measure reaction speed.
  • Save index.html and refresh your browser.
  • Click the game box. It should turn red and display "Wait for green..." for 1-4 seconds, then turn green with "Click NOW!" displayed.
  • Try clicking while the box is still red. You should see "Too early! Click to try again." and the box stays red.

Nothing happens when you click?

  • Make sure the startRound function is placed after the closing }); of your addEventListener block, not inside it.
  • Check that your <script> tag is properly closed with </script> before </body>.
  • Open your browser's developer console (press Cmd+Option+J on macOS or Ctrl+Shift+J on Windows) and look for red error messages that point to the problem.

Help me debug why my game box isn't responding to clicks.

✔️ Awesome, I've got everything!

Great. Double check you've saved your file before moving on.

ⓧ I'd like to double check the full code

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Reaction Speed Game</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            background-color: #1a1a2e;
            color: #ffffff;
        }

        .game-container {
            text-align: center;
            width: 100%;
            max-width: 600px;
            padding: 20px;
        }

        h1 {
            font-size: 2rem;
            margin-bottom: 10px;
        }

        .instructions {
            font-size: 1.1rem;
            color: #aaaaaa;
            margin-bottom: 20px;
        }

        .game-box {
            width: 100%;
            height: 300px;
            border-radius: 16px;
            display: flex;
            justify-content: center;
            align-items: center;
            cursor: pointer;
            background-color: #16213e;
            transition: background-color 0.1s;
            margin-bottom: 20px;
            user-select: none;
        }

        .game-box .message {
            font-size: 1.5rem;
            font-weight: bold;
            padding: 20px;
        }

        .stats {
            display: flex;
            justify-content: space-around;
            gap: 20px;
        }

        .stat-box {
            background-color: #16213e;
            padding: 15px 25px;
            border-radius: 12px;
            flex: 1;
        }

        .stat-box .label {
            font-size: 0.85rem;
            color: #aaaaaa;
            margin-bottom: 5px;
        }

        .stat-box .value {
            font-size: 1.4rem;
            font-weight: bold;
        }
    </style>
</head>
<body>
    <div class="game-container">
        <h1>Reaction Speed Game</h1>
        <p class="instructions">Click the box when it turns green. Don't click too early!</p>
        <div class="game-box" id="gameBox">
            <span class="message" id="message">Click to Start</span>
        </div>
        <div class="stats">
            <div class="stat-box">
                <div class="label">Last Time</div>
                <div class="value" id="lastTime">- ms</div>
            </div>
            <div class="stat-box">
                <div class="label">Best Time</div>
                <div class="value" id="bestTime">- ms</div>
            </div>
            <div class="stat-box">
                <div class="label">Round</div>
                <div class="value" id="round">0</div>
            </div>
        </div>
    </div>

    <script>
        var gameBox = document.getElementById('gameBox');
        var message = document.getElementById('message');
        var lastTimeDisplay = document.getElementById('lastTime');
        var bestTimeDisplay = document.getElementById('bestTime');
        var roundDisplay = document.getElementById('round');

        var state = 'idle';
        var timeoutId = null;
        var startTime = 0;
        var bestTime = Infinity;
        var roundCount = 0;

        gameBox.addEventListener('click', function () {
            if (state === 'idle') {
                startRound();
            } else if (state === 'waiting') {
                clearTimeout(timeoutId);
                gameBox.style.backgroundColor = '#e74c3c';
                message.textContent = 'Too early! Click to try again.';
                state = 'idle';
            } else if (state === 'ready') {
                var reactionTime = Math.round(Date.now() - startTime);
            }
        });

        function startRound() {
            state = 'waiting';
            gameBox.style.backgroundColor = '#e74c3c';
            message.textContent = 'Wait for green...';

            var delay = Math.floor(Math.random() * 3000) + 1000;

            timeoutId = setTimeout(function () {
                state = 'ready';
                gameBox.style.backgroundColor = '#2ecc71';
                message.textContent = 'Click NOW!';
                startTime = Date.now();
            }, delay);
        }
    </script>
</body>
</html>

Your game is interactive. You can start rounds, get caught clicking too early, and see the box turn green at random times. Next up, you'll track scores and polish the experience so the player sees their reaction time and best score.

Track Scores and Polish

In the last step, you wired up the game's core logic. The click handler knows when you click too early and can calculate your reaction time.

Right now, though, the reaction time just disappears into the void. The stats panel sits empty, and the game box gets stuck after one round. In this step, you'll close the loop by updating the scoreboard and resetting the game for the next round.

In this step, get ready to:
  • Update the stats panel with reaction times and round count.
  • Reset the game box so the player can go again.
  • Test the complete game end to end.
Update the stats display

The game already calculates your reaction time. Now you need to push that number into the stats panel so the player can see their score.

  • In index.html, scroll down to the <script> section and find this line inside the else if (state === 'ready') block:
var reactionTime = Math.round(Date.now() - startTime);
  • Copy the following code directly below that line:
                roundCount++;
                roundDisplay.textContent = roundCount;
                lastTimeDisplay.textContent = reactionTime + ' ms';

                if (reactionTime < bestTime) {
                    bestTime = reactionTime;
                    bestTimeDisplay.textContent = bestTime + ' ms';
                }

What does this code do?

This block handles all the score tracking after a successful click.

  • roundCount++ adds 1 to the round counter, then roundDisplay.textContent pushes that number into the page.
  • lastTimeDisplay.textContent shows the player's reaction time in milliseconds.
  • The if (reactionTime < bestTime) check compares this round to the all-time best. If it's faster, both the variable and the display get updated.
  • Save index.html and refresh your browser.
  • Click the box, wait for green, then click again. You should see the Last Time and Round stats update. The game box will stay green because we haven't added the reset yet.

Stats not updating?

  • Make sure the new code is inside the else if (state === 'ready') block, directly after the var reactionTime line.
  • Check for missing semicolons or brackets. A single typo can silently break everything below it.
  • Open your browser's developer console (right-click the page, then click Inspect, then Console) to see if any errors appear.

Why aren't my stats updating when I click on green?

Reset the game box after each round

After showing the result, the game needs to return to its starting state so the player can go again. Without this reset, the game is stuck after one round.

  • In the same else if (state === 'ready') block, find the closing } of the if (reactionTime < bestTime) check you just added.
  • Copy the following code directly below that closing brace:
                gameBox.style.backgroundColor = '#16213e';
                message.textContent = reactionTime + ' ms! Click to play again.';
                state = 'idle';

What does this code do?

  • gameBox.style.backgroundColor = '#16213e' sets the box back to its original dark color.
  • message.textContent shows the player their time and invites them to play again.
  • state = 'idle' resets the state machine so the next click starts a new round.
  • Save index.html and refresh your browser.
  • Play a round. After you click on green, the box should return to dark blue and display your reaction time with "Click to play again." visible.

Game box staying green after you click?

  • Make sure the three reset lines are inside the else if (state === 'ready') block, after the if (reactionTime < bestTime) check's closing brace.
  • Check that you haven't accidentally placed the reset code outside the click handler's closing braces.

Why is my game box staying green instead of resetting?

Test the complete game

Your game is now fully wired up. Time to test every path and make sure nothing breaks.

  • Refresh your browser to start fresh.
  • Click the box to start a round. Wait for it to turn green, then click.
  • Confirm your reaction time appears in the Last Time stat and the Round counter shows 1.
  • Play a second round. If your time is faster, confirm the Best Time stat updates.
  • Start a round, then click the box while it's still red. Confirm you see "Too early! Click to try again." and the round counter does NOT increment.

What should I see?

A complete game loop looks like this: dark box with "Click to Start" turns red with "Wait for green...", then turns green with "Click NOW!". After you click green, the box goes dark again and shows your time (e.g. "237 ms! Click to play again."). The stats panel updates below.

The Best Time only changes when you beat your previous record. The Round counter only increments on successful clicks, not on "too early" attempts.

✔️ Awesome, I've got everything!

Great. Double check you've saved your file.

ⓧ I'd like to double check the full code

Here is the complete index.html file. Compare it against yours to spot any differences.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Reaction Speed Game</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            background-color: #1a1a2e;
            color: #ffffff;
        }

        .game-container {
            text-align: center;
            width: 100%;
            max-width: 600px;
            padding: 20px;
        }

        h1 {
            font-size: 2rem;
            margin-bottom: 10px;
        }

        .instructions {
            font-size: 1.1rem;
            color: #aaaaaa;
            margin-bottom: 20px;
        }

        .game-box {
            width: 100%;
            height: 300px;
            border-radius: 16px;
            display: flex;
            justify-content: center;
            align-items: center;
            cursor: pointer;
            background-color: #16213e;
            transition: background-color 0.1s;
            margin-bottom: 20px;
            user-select: none;
        }

        .game-box .message {
            font-size: 1.5rem;
            font-weight: bold;
            padding: 20px;
        }

        .stats {
            display: flex;
            justify-content: space-around;
            gap: 20px;
        }

        .stat-box {
            background-color: #16213e;
            padding: 15px 25px;
            border-radius: 12px;
            flex: 1;
        }

        .stat-box .label {
            font-size: 0.85rem;
            color: #aaaaaa;
            margin-bottom: 5px;
        }

        .stat-box .value {
            font-size: 1.4rem;
            font-weight: bold;
        }
    </style>
</head>
<body>
    <div class="game-container">
        <h1>Reaction Speed Game</h1>
        <p class="instructions">Click the box when it turns green. Don't click too early!</p>
        <div class="game-box" id="gameBox">
            <span class="message" id="message">Click to Start</span>
        </div>
        <div class="stats">
            <div class="stat-box">
                <div class="label">Last Time</div>
                <div class="value" id="lastTime">- ms</div>
            </div>
            <div class="stat-box">
                <div class="label">Best Time</div>
                <div class="value" id="bestTime">- ms</div>
            </div>
            <div class="stat-box">
                <div class="label">Round</div>
                <div class="value" id="round">0</div>
            </div>
        </div>
    </div>

    <script>
        var gameBox = document.getElementById('gameBox');
        var message = document.getElementById('message');
        var lastTimeDisplay = document.getElementById('lastTime');
        var bestTimeDisplay = document.getElementById('bestTime');
        var roundDisplay = document.getElementById('round');

        var state = 'idle';
        var timeoutId = null;
        var startTime = 0;
        var bestTime = Infinity;
        var roundCount = 0;

        gameBox.addEventListener('click', function () {
            if (state === 'idle') {
                startRound();
            } else if (state === 'waiting') {
                clearTimeout(timeoutId);
                gameBox.style.backgroundColor = '#e74c3c';
                message.textContent = 'Too early! Click to try again.';
                state = 'idle';
            } else if (state === 'ready') {
                var reactionTime = Math.round(Date.now() - startTime);
                roundCount++;
                roundDisplay.textContent = roundCount;
                lastTimeDisplay.textContent = reactionTime + ' ms';

                if (reactionTime < bestTime) {
                    bestTime = reactionTime;
                    bestTimeDisplay.textContent = bestTime + ' ms';
                }

                gameBox.style.backgroundColor = '#16213e';
                message.textContent = reactionTime + ' ms! Click to play again.';
                state = 'idle';
            }
        });

        function startRound() {
            state = 'waiting';
            gameBox.style.backgroundColor = '#e74c3c';
            message.textContent = 'Wait for green...';

            var delay = Math.floor(Math.random() * 3000) + 1000;

            timeoutId = setTimeout(function () {
                state = 'ready';
                gameBox.style.backgroundColor = '#2ecc71';
                message.textContent = 'Click NOW!';
                startTime = Date.now();
            }, delay);
        }
    </script>
</body>
</html>

Secret mission

Add Difficulty Levels

Your game works, but every round feels the same. What if the game got harder as you improved? In this secret mission, you'll add an automatic difficulty system that shortens the wait time and changes the target color as you progress through rounds.

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

This project runs entirely locally on your computer, so there are no ongoing costs.

Resources you used:

  • The reaction-game folder on your Desktop containing index.html.

Keep everything running

No action needed. Choose this if you want to keep playing the game or plan to build on it further.

  • Your reaction-game folder stays on your Desktop.
  • Double-click index.html any time to play the game in your browser.

Pause - I'll come back to this later

Since this is a local file with no running processes, there is nothing to pause. The game only runs when you have it open in your browser.

  • Close the browser tab if you have the game open.
  • Your files remain on your Desktop for whenever you want to return.

Delete - I don't want to use this again

Remove all project files from your computer.

  • Close index.html in your code editor if it is still open.
  • Close the browser tab if the game is still open.
  • Delete the reaction-game folder from your Desktop by running this command in your terminal:

macOS

rm -rf ~/Desktop/reaction-game

Windows

rmdir /s /q %USERPROFILE%\Desktop\reaction-game

Confirm the folder is gone by checking your Desktop. You should no longer see the reaction-game folder.

Nice Work!

Nice Work!

You just built a complete interactive reaction speed game with vanilla JavaScript. From a blank HTML file to a fully functional game that tracks your scores and challenges your reflexes.

You've learned how to:

  • Selected and manipulated DOM elements using document.getElementById() and updated the page dynamically with textContent and style properties.
  • Responded to user input with event listeners using addEventListener('click', ...) and handled different outcomes based on game state.
  • Controlled timing with setTimeout and clearTimeout to create a random-delay challenge and managed application state with variables and conditional logic.
  • Added a difficulty progression system that automatically increases the challenge every 3 rounds with dynamic visual feedback.

These are the exact same patterns used in every web application, from simple landing pages to complex apps. You now have the foundation to build anything interactive on the web.

Ready to quiz yourself?