Reaction Speed Tester

In this advanced project, we’ll create a reaction speed testing game that measures how quickly you can respond to visual cues. The game uses a 7-segment display to show your reaction time and provides multiple rounds of testing with performance feedback!

Circuit

../_images/P8.png

Sketch

// Reaction-Speed Tester – 7-Segment Display Edition
import processing.io.*;

// 74HC595 pins
int SDI   = 17;   // serial data in
int RCLK  = 18;   // register clock (latch)
int SRCLK = 27;   // shift-clock

// common-cathode segment codes 0-9
int[] segCode = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f};

// game states
int state = 0;
int start, reaction = 0;
int best = 9999;
boolean spacePressed = false;
int round = 1;
int maxRounds = 5;

// countdown
int cdStart, cdDuration;

void setup() {
    size(600, 400);
    frameRate(60);

    GPIO.pinMode(SDI,   GPIO.OUTPUT);
    GPIO.pinMode(RCLK,  GPIO.OUTPUT);
    GPIO.pinMode(SRCLK, GPIO.OUTPUT);

    hc595_shift(segCode[0]);
}

void draw() {
    switch (state) {
        case 0: drawWelcome(); break;
        case 1: drawCountdown(); break;
        case 2: drawWait(); break;
        case 3: drawReaction(); break;
        case 4: drawResult(); break;
    }
    spacePressed = false;
}

// ------------------------------------------------------------------
//  SCREENS
// ------------------------------------------------------------------
void drawWelcome() {
    background(50, 50, 100);

    fill(255, 255, 100);
    textAlign(CENTER);
    textSize(48);
    text("Reaction Test", width / 2, height / 2 - 100);

    textSize(24);
    fill(255);
    text("Test your reaction speed!", width / 2, height / 2 - 40);
    text("Press SPACE as soon as you see 'GO!'", width / 2, height / 2 - 10);
    text("7-segment shows your time", width / 2, height / 2 + 20);

    textSize(20);
    fill(100, 255, 100);
    text("Round " + round + " / " + maxRounds, width / 2, height / 2 + 60);

    textSize(32);
    fill(255, 100, 100);
    text("Press SPACE to start!", width / 2, height / 2 + 120);

    if (best < 9999) {
        textSize(18);
        fill(255, 255, 0);
        text("Best: 0." + nf(best, 3) + " s", width / 2, height / 2 + 160);
    }

    hc595_shift(segCode[0]);

    if (spacePressed) {
        state = 1;
        cdStart = millis();
        cdDuration = int(random(2000, 5000));
    }
}

void drawCountdown() {
    background(100, 50, 50);

    fill(255, 100, 100);
    textAlign(CENTER);
    textSize(72);
    text("Ready...", width / 2, height / 2 - 50);

    textSize(24);
    fill(255, 200, 200);
    text("Wait for GO!", width / 2, height / 2 + 30);
    text("Don’t press early!", width / 2, height / 2 + 60);

    int elapsed = millis() - cdStart;
    int remain  = max(0, (cdDuration - elapsed) / 1000 + 1);
    if (remain > 0) hc595_shift(segCode[min(remain, 9)]);

    if (spacePressed) {
        state = 4;
        reaction = -1; // false start
        return;
    }

    if (elapsed >= cdDuration) {
        state = 2;
        start = millis();
    }
}

void drawWait() {
    background(100, 100, 50);

    fill(255, 255, 100);
    textAlign(CENTER);
    textSize(96);
    text("GO!", width / 2, height / 2);

    textSize(24);
    fill(255, 255, 200);
    text("Press SPACE now!", width / 2, height / 2 + 80);

    hc595_shift(segCode[8]); // show 8 for GO!

    if (spacePressed) {
        reaction = millis() - start;
        state = 3;
    }

    if (millis() - start > 2000) {
        reaction = 9999; // timeout
        state = 4;
    }
}

void drawReaction() {
    background(50, 100, 50);

    fill(100, 255, 100);
    textAlign(CENTER);
    textSize(48);
    text("Reaction:", width / 2, height / 2 - 40);

    textSize(72);
    text("0." + nf(reaction, 3) + " s", width / 2, height / 2 + 20);

    int digit = (reaction / 100) % 10;
    hc595_shift(segCode[digit]);

    if (millis() - start > 2000) state = 4;
}

void drawResult() {
    if (reaction == -1) {           // false start
        background(150, 50, 50);
        fill(255, 100, 100);
        textAlign(CENTER);
        textSize(48);
        text("False Start!", width / 2, height / 2 - 40);
        textSize(24);
        text("Wait for the GO! signal", width / 2, height / 2 + 20);
        hc595_shift(segCode[0]);
    } else if (reaction >= 9999) {  // timeout
        background(100, 50, 50);
        fill(255, 150, 100);
        textAlign(CENTER);
        textSize(48);
        text("Too Slow!", width / 2, height / 2 - 40);
        textSize(24);
        text("> 2 s timeout", width / 2, height / 2 + 20);
        hc595_shift(segCode[9]);
    } else {                        // valid result
        background(50, 100, 100);
        fill(100, 255, 255);
        textAlign(CENTER);
        textSize(36);
        text("Time: 0." + nf(reaction, 3) + " s", width / 2, height / 2 - 60);

        if (reaction < best) {
            best = reaction;
            fill(255, 255, 100);
            textSize(24);
            text("🎉 New Record! 🎉", width / 2, height / 2 - 20);
        }

        String rating = "";
        if (reaction < 200) rating = "Lightning!";
        else if (reaction < 300) rating = "Very fast!";
        else if (reaction < 400) rating = "Fast!";
        else if (reaction < 500) rating = "Good!";
        else rating = "Keep practising";

        fill(255);
        textSize(20);
        text(rating, width / 2, height / 2 + 20);

        int digit = (reaction / 100) % 10;
        hc595_shift(segCode[digit]);
    }

    // next or finish
    fill(200);
    textSize(18);
    if (round < maxRounds) {
        text("Press SPACE for next round (" + (round + 1) + "/" + maxRounds + ")",
            width / 2, height / 2 + 80);
        if (spacePressed) {
            round++;
            state = 0;
        }
    } else {
        text("Done! Best time: 0." + nf(best, 3) + " s", width / 2, height / 2 + 80);
        text("Press R to restart", width / 2, height / 2 + 110);
    }
}

// ------------------------------------------------------------------
//  74HC595 helper
// ------------------------------------------------------------------
void hc595_shift(int data) {
    for (int i = 0; i < 8; i++) {
        int bit = (data << i) & 0x80;
        GPIO.digitalWrite(SDI, bit != 0 ? GPIO.HIGH : GPIO.LOW);
        GPIO.digitalWrite(SRCLK, GPIO.HIGH);
        delay(1);
        GPIO.digitalWrite(SRCLK, GPIO.LOW);
    }
    GPIO.digitalWrite(RCLK, GPIO.HIGH);
    delay(1);
    GPIO.digitalWrite(RCLK, GPIO.LOW);
}

// ------------------------------------------------------------------
//  INPUT
// ------------------------------------------------------------------
void keyPressed() {
    if (key == ' ') spacePressed = true;
    if (key == 'r' || key == 'R') {
        round = 1;
        best = 9999;
        state = 0;
    }
}
../_images/7.png

How it works?

This reaction speed tester demonstrates advanced game programming and precise timing measurement:

Game State Management: - 5 Game States: Welcome (0), Countdown (1), Wait for GO (2), Show Reaction (3), Results (4) - State Machine: Clean transitions between different game phases - Round System: 5 rounds of testing with progress tracking - Session Memory: Tracks best time across all rounds

Reaction Time Measurement: - Precise Timing: Uses millis() for millisecond-accurate measurements - Random Delays: 2-5 second countdown prevents anticipation cheating - False Start Detection: Penalizes early button presses during countdown - Timeout Handling: 2-second limit prevents indefinite waiting

7-Segment Display Integration: - 74HC595 Control: Uses shift register for efficient 7-segment driving - Dynamic Display: Shows countdown numbers, GO signal (8), and reaction digits - Meaningful Feedback: Displays the tens-of-milliseconds digit (0.1-0.9 seconds) - Status Indication: Different numbers for different game states

User Interface Design: - Multi-Screen Layout: Professional game flow with distinct visual phases - Color-Coded States: Different background colors for each game phase - Performance Feedback: Ratings from “Lightning!” to “Keep practising” - Progress Tracking: Shows current round and session statistics

Advanced Timing Logic: The core timing calculation works as follows: - Countdown Phase: Random 2-5 second delay prevents pattern recognition - GO Signal: Precisely records the moment GO appears - Response Detection: Measures exact time between GO and spacebar press - Accuracy: Displays reaction time as 0.XXX seconds format

Game Features: - False Start Protection: Pressing space during countdown triggers penalty - New Record Celebration: Visual feedback for personal best times - Performance Rating System: Contextual feedback based on reaction speed - Session Statistics: Tracks and displays best time across all rounds

Hardware Synchronization: - Real-time Display: 7-segment shows relevant information for each game state - Immediate Feedback: Display updates instantly with game state changes - Visual Consistency: Screen and hardware display work together

Programming Concepts: - State Machines: Clean game flow management - Precise Timing: Millisecond-accurate measurements - Hardware Control: GPIO and shift register manipulation - User Experience: Professional game design principles - Data Validation: False start and timeout error handling

Educational Value: - Reaction Science: Learn about human response times and factors affecting them - Timing Systems: Understand how computers measure precise time intervals - Game Design: Experience complete game development lifecycle - Hardware Integration: See how software and hardware work together

This project bridges gaming, science, and engineering - perfect for understanding both human performance and computer precision!