A two-player reaction game played on a single strip of WS2812B LEDs. Players compete by pressing their button at the right moment when the ball enters their zone.
- Fast-paced two-player gameplay
- Dynamic difficulty: zones shrink after each point
- Early-hit bonus: hit the ball as it enters your zone for extra speed
- 13 attract mode animations to draw players in
- Modular animation system for easy customization
- Visual feedback for hits, misses, and scoring
- Configurable game parameters
| Component | Specification | Quantity |
|---|---|---|
| Microcontroller | WEMOS D1 Mini ESP32 | 1 |
| LED Strip | WS2812B, 55 LEDs | 1 |
| Buttons | Momentary push button | 2 |
| Power Supply | 5V, 3A recommended | 1 |
| Wires | Jumper wires | As needed |
| Component | ESP32 Pin |
|---|---|
| LED Data | GPIO 5 |
| Left Button | GPIO 17 |
| Right Button | GPIO 18 |
| Left Button LED | GPIO 25 (PWM) |
| Right Button LED | GPIO 26 (PWM) |
| LED Power | 5V |
| LED Ground | GND |
| Buttons | GND (active LOW with internal pull-up) |
-
Clone the repository:
git clone https://github.com/yourusername/1d-pong.git cd 1d-pong -
Open the project in VS Code:
-
Build the project:
- Click the checkmark icon in the PlatformIO toolbar, or
- Press
Ctrl+Alt+B
-
Upload to your ESP32:
- Connect your ESP32 via USB
- Click the arrow icon in the PlatformIO toolbar, or
- Press
Ctrl+Alt+U
Edit include/config.h to customize:
// Hardware
#define NUM_LEDS 55 // Number of LEDs in your strip
#define LED_PIN 5 // Data pin for LED strip
#define BUTTON_LEFT_PIN 17 // Left player button
#define BUTTON_RIGHT_PIN 18 // Right player button
// Button LEDs (PWM capable pins)
#define BUTTON_LED_LEFT_PIN 25 // Left button LED
#define BUTTON_LED_RIGHT_PIN 26 // Right button LED
#define BUTTON_LED_PWM_FREQ 5000 // 5kHz PWM frequency
#define BUTTON_LED_PWM_RES 8 // 8-bit resolution (0-255)
// Game Parameters
#define ZONE_SIZE_START 10 // Initial zone size (LEDs)
#define ZONE_SIZE_MIN 5 // Minimum zone size
#define SCORE_TO_WIN 5 // Points to win a match
// Speed
#define BALL_INITIAL_DELAY_MS 60 // Starting ball speed (lower = faster)
// Animation
#define ANIMATION_DURATION_MS 10000UL // Duration per animation (ms)- When idle, the LED strip displays attract mode animations
- Either player presses their button to start a match
- The ball spawns in the center after a countdown
- The ball (white LED) travels toward one player's zone
- When the ball enters your zone (colored section), press your button to return it
- The ball speeds up with each successful return
- Score a point when your opponent misses
- Miss: Ball exits the strip = opponent scores
- Early press: Pressing outside your zone = opponent scores
- First to 5 points wins the match
- Early-hit bonus: Hitting the ball as it enters your zone (not waiting until it's about to exit) adds extra speed, making it harder for your opponent
- Shrinking zones: After each point, both zones shrink by 1 LED, increasing difficulty
stateDiagram-v2
[*] --> IDLE
IDLE --> SERVE : Button Press
SERVE --> BALL_MOVING : Countdown Complete
BALL_MOVING --> BALL_MOVING : Successful Hit
BALL_MOVING --> CHECK_GAME_OVER : Miss or Penalty
CHECK_GAME_OVER --> SERVE : Score < 5
CHECK_GAME_OVER --> GAME_OVER : Score ≥ 5
GAME_OVER --> IDLE : Win Animation Complete
note right of IDLE
Attract mode animations
Button LED breathing & pulses
end note
note right of SERVE
Countdown with visual pulse
Zone shrinks after each point
end note
note right of BALL_MOVING
Active gameplay
Button LEDs indicate active zones
Flash on hit, blink on miss
end note
note right of GAME_OVER
Display winner animation
First to 5 points wins
end note
| State | Description |
|---|---|
IDLE |
Attract mode - cycling through animations, waiting for player |
SERVE |
Countdown before ball launch, zones displayed |
BALL_MOVING |
Active gameplay - ball moving, checking for hits |
CHECK_GAME_OVER |
Evaluate if a player has won |
GAME_OVER |
Display winner animation, then return to idle |
The animation system uses auto-registration - simply create a new .cpp file in src/animations/ and it will be automatically included.
-
Copy the template:
cp src/animations/_template.cpp.example src/animations/my_animation.cpp
-
Edit your new file:
#include "animation.h" class MyAnimation : public Animation { public: MyAnimation(const char* name) : Animation(name) {} void reset() override { // Initialize state when animation starts _lastUpdate = 0; } void update(CRGB* leds, uint8_t numLeds) override { // Non-blocking timing if (millis() - _lastUpdate < 50) return; _lastUpdate = millis(); // Your animation logic here fill_solid(leds, numLeds, CRGB::Black); // ... FastLED.show(); } private: uint32_t _lastUpdate = 0; }; // Register with a display name REGISTER_ANIMATION(MyAnimation, "My Animation");
-
Build and upload - your animation is now in the rotation!
- Non-blocking: Never use
delay()- usemillis()for timing - Call
FastLED.show(): Required to display your changes - Use
reset(): Initialize state variables when animation starts - Available helpers:
fill_solid(),CHSV(),sin8(),qadd8(),qsub8(), etc.
| Animation | Description |
|---|---|
| Rainbow Dot | Rainbow background with bouncing white dot |
| Pong Demo | Simulated game demonstration |
| Plasma Comet | Plasma background with white comet trail |
| Cylon | Scanning red eye effect |
| Fire | Flames from both player zones |
| Sparkle | Twinkling stars with shooting stars |
| Duel Chase | Blue/green dots colliding |
| Heartbeat | Pulsing red heartbeat |
| Ocean Wave | Blue sine waves with foam |
| Bouncing Balls | Physics-based bouncing balls |
| Matrix Rain | Green digital rain |
| Lightning Storm | Random flashes with thunder |
| Color Breathing | Slow color pulsing |
- Check power supply voltage (5V) and amperage (3A recommended for 55 LEDs)
- Verify data pin connection (GPIO 5 by default)
- Ensure correct LED type in config (
WS2812B) - Check color order setting (
GRBfor most WS2812B strips)
- Verify button connections to correct GPIO pins
- Buttons should connect between GPIO and GND (uses internal pull-up)
- Check for loose connections
- Try holding BOOT button on ESP32 while uploading
- Check USB cable (some are power-only)
- Verify correct board selected in
platformio.ini
- Adjust
BALL_INITIAL_DELAY_MSinconfig.h - Higher values = slower ball
- Recommended range: 40-80ms
1d-pong/
├── platformio.ini # PlatformIO configuration
├── include/
│ ├── config.h # Game and hardware configuration
│ └── animation.h # Animation base class
├── src/
│ ├── main.cpp # Game logic and state machine
│ ├── animation.cpp # Animation manager
│ └── animations/ # Animation implementations
│ ├── _template.cpp.example
│ ├── rainbow_dot.cpp
│ ├── fire.cpp
│ └── ...
└── README.md
This project was developed with assistance from AI tools (Claude, ChatGPT, etc.). All code has been reviewed and tested by the author.
Contributions are welcome! Here's how you can help:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-animation) - Commit your changes (
git commit -m 'Add amazing animation') - Push to the branch (
git push origin feature/amazing-animation) - Open a Pull Request
- New attract mode animations
- Sound effects support (buzzer/speaker)
- Web-based configuration interface
- Score persistence (EEPROM)
- Tournament mode (best of N matches)
- Single-player mode with AI
This project was inspired by a 1D Pong game seen at 38C3 (38th Chaos Communication Congress).
Built with:
This project is licensed under the MIT License - see below for details.
MIT License
Copyright (c) 2025
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

