
What you'll build
In this guide you will construct an arcade-style reaction tower using an ESP32, a WS2812B LED ring, a large arcade push button, a piezo buzzer, and an OLED score display. The game lights up a random LED on the ring as a cue, and you must slap the arcade button as fast as possible before the light moves on. Each successful hit earns points based on how quickly you reacted -- measured in milliseconds -- while a miss triggers a buzzer penalty sound and resets your combo multiplier. The OLED shows your current score, best reaction time, and combo streak in real time, and a victory fanfare plays when you beat your high score.
Arcade games are one of the most rewarding categories of maker projects because the feedback loop is immediate and physical. This build teaches you microsecond-precision timing with the ESP32's hardware timers, interrupt-driven button detection for accurate reaction measurement, random number generation for unpredictable cue placement, and smooth LED animations that provide clear visual feedback without blocking the main game loop. You will also implement a simple scoring algorithm with combo multipliers and persistent high-score storage in flash memory so your best run survives a power cycle.
The completed tower is a genuinely addictive tabletop game that friends and family will want to play repeatedly. The modular code makes it easy to add difficulty levels that shorten the reaction window, introduce multi-color cues where only a specific color should be hit, or chain multiple towers together over ESP-NOW for a competitive multiplayer experience. It is an ideal beginner project that produces something immediately fun while teaching timing, interrupts, and state management patterns that are essential for any real-time embedded application. For another interactive game prop, try the RFID treasure chest which combines NFC tag reading with a servo-driven lock mechanism.
Wiring diagram
Wiring diagram
Components needed
Assembly
Wire game controls
Connect LED ring DIN to GPIO4, arcade button to GPIO27, and buzzer to GPIO26.
- Use a pull-up button wiring style for stable input.
Attach score display
Wire OLED SDA/SCL to GPIO21/GPIO22 and start reaction rounds.
- Press quickly after target light appears for high score.
Pin assignments
| Pin | Connection | Type |
|---|---|---|
| GPIO 4 | arcade-led-ring-1 DIN | DATA |
| GPIO 27 | arcade-button-1 SIG | DIGITAL |
| GPIO 26 | arcade-buzzer-1 SIG | PWM |
| GPIO 21 | arcade-oled-1 SDA | I2C |
| GPIO 22 | arcade-oled-1 SCL | I2C |
Code
#include <Wire.h>
#include <FastLED.h>
#include <Adafruit_SSD1306.h>
#define LED_PIN 4
#define NUM_LEDS 16
#define BTN_PIN 27
#define BUZZER_PIN 26
#define SDA_PIN 21
#define SCL_PIN 22
CRGB leds[NUM_LEDS];
Adafruit_SSD1306 display(128, 64, &Wire, -1);
unsigned long roundStart = 0;
bool waitingForPress = false;
int score = 0;
void setup() {
Wire.begin(SDA_PIN, SCL_PIN);
FastLED.addLeds<NEOPIXEL, LED_PIN>(leds, NUM_LEDS);
FastLED.setBrightness(140);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
pinMode(BTN_PIN, INPUT_PULLUP);
pinMode(BUZZER_PIN, OUTPUT);
randomSeed(micros());
}
void loop() {
if (!waitingForPress) {
int target = random(NUM_LEDS);
fill_solid(leds, NUM_LEDS, CRGB::Black);
leds[target] = CRGB::Orange;
FastLED.show();
roundStart = millis();
waitingForPress = true;
}
if (!digitalRead(BTN_PIN) && waitingForPress) {
int reactionMs = (int)(millis() - roundStart);
score += max(0, 1500 - reactionMs);
tone(BUZZER_PIN, reactionMs < 350 ? 2200 : 1200, 70);
display.clearDisplay();
display.setCursor(0, 0);
display.printf("Reaction: %dms\nScore: %d", reactionMs, score);
display.display();
waitingForPress = false;
delay(350);
}
}
// Run this and build other cool things at schematik.ioReady to build this?
Open this project in Schematik to get the full wiring diagram, pin assignments, and deployable code for the Arcade Reaction Tower.
Open in Schematik →


