Binary Loop is an educational hardware + software puzzle game that teaches binary logic and XOR cryptography through a gamified, cyberpunk-themed experience. Players solve XOR decryption puzzles on a physical ESP32 device, then verify their scores on a web leaderboard using cryptographic tokens.
╔══════════════════════════════════════════════╗
║ KEY: 0 0 1 1 ║
║ CIPHER: 1 0 1 0 ║
║ ───────────────── ║
║ PLAIN: [█] _ _ _ ← You solve this! ║
╚══════════════════════════════════════════════╝
- Gameplay Demo
- How It Works
- System Architecture
- Hardware Setup
- Software Installation
- How to Play
- Token Verification System
- API Reference
- Project Structure
- Troubleshooting
- Contributing
- License
Binary Loop bridges the physical and digital worlds. A standalone ESP32 device runs the puzzle game offline, and a PHP web dashboard handles score verification and leaderboard display.
flowchart LR
subgraph HARDWARE["🔌 ESP32 Device (Offline)"]
A["🟦 Press BLUE\nto Start"] --> B["🧩 Solve XOR\nPuzzles"]
B --> C{"All 30 Levels\nCompleted?"}
C -->|Yes| D["🔑 Generate\n4-Digit Token"]
C -->|"🟥 Press RED\n(Give Up)"| D
end
subgraph WEB["🌐 Web Dashboard (PHP)"]
E["📱 Scan QR Code"] --> F["📝 Enter Name\n& Token"]
F --> G["🔐 Server Verifies\nToken"]
G -->|Valid| H["✅ Score Added\nto Leaderboard"]
G -->|Invalid| I["❌ Score = 0\n(Anti-Cheat)"]
end
D -.->|"Student reads\ntoken from screen"| E
style HARDWARE fill:#0a0a0a,stroke:#00ff41,stroke-width:2px,color:#00ff41
style WEB fill:#0a0a0a,stroke:#00d9ff,stroke-width:2px,color:#00d9ff
graph TB
subgraph ESP32["ESP32 Microcontroller"]
FW["Firmware (C++/Arduino)"]
OLED["OLED SSD1306\n128×64 I2C"]
BTN_B["🟦 Blue Button\nGPIO 26"]
BTN_R["🟥 Red Button\nGPIO 25"]
TOK["Token Generator\n(Score × 7919 + Level × 123) % 10000"]
BTN_B --> FW
BTN_R --> FW
FW --> OLED
FW --> TOK
end
subgraph Server["PHP Web Server"]
API["api.php\nREST API"]
VER["Token Verifier\nBrute-Force Reverse Lookup"]
DB["leaderboard.json\nScore Storage"]
API --> VER
VER --> DB
end
subgraph Client["Web Frontend"]
UI["Cyberpunk UI\nHTML/CSS/JS"]
FORM["Score Submission\nForm"]
LB["Live Leaderboard\nTable"]
UI --> FORM
UI --> LB
end
TOK -.->|"4-digit token\n(air-gapped)"| FORM
FORM -->|"POST /api.php?action=submit"| API
LB -->|"GET /api.php?action=leaderboard"| API
style ESP32 fill:#1a0a0a,stroke:#ff6b35,stroke-width:2px,color:#ff6b35
style Server fill:#0a0a1a,stroke:#777BB4,stroke-width:2px,color:#777BB4
style Client fill:#0a1a0a,stroke:#00ff41,stroke-width:2px,color:#00ff41
| Component | Specification | Quantity |
|---|---|---|
| ESP32 Dev Module | Dual-Core 240MHz, WiFi/BLE | 1 |
| OLED Display | SSD1306, 0.96", 128×64, I2C | 1 |
| Push Button (Blue) | Momentary, normally open | 1 |
| Push Button (Red) | Momentary, normally open | 1 |
| Jumper Wires | Male-to-Male | ~8 |
| Breadboard | Standard size | 1 |
┌──────────────────────┐
│ ESP32 │
│ │
OLED SSD1306 │ │ Buttons
┌──────────┐ │ │ ┌──────────┐
│ SDA ─────┼────┤ GPIO 2 (D2) │ │ │
│ SCL ─────┼────┤ GPIO 15 (D15) │ │ BLUE ────┼──── GPIO 26 (D26)
│ VCC ─────┼────┤ 3.3V │ │ GND ───┼──── GND
│ GND ─────┼────┤ GND │ │ │
└──────────┘ │ │ │ RED ─────┼──── GPIO 25 (D25)
│ │ │ GND ───┼──── GND
│ │ └──────────┘
└──────────────────────┘
Note: Buttons use internal pull-up resistors (INPUT_PULLUP).
Wire one pin to GPIO, the other to GND. No external resistors needed.
| Component | ESP32 Pin | Type | Notes |
|---|---|---|---|
| OLED SDA | GPIO 2 | I2C Data | Custom I2C pins |
| OLED SCL | GPIO 15 | I2C Clock | Custom I2C pins |
| OLED VCC | 3.3V | Power | Do NOT use 5V |
| OLED GND | GND | Ground | — |
| Blue Button | GPIO 26 | Digital Input | Internal pull-up, active LOW |
| Red Button | GPIO 25 | Digital Input | Internal pull-up, active LOW |
| Tool | Purpose | Download |
|---|---|---|
| VS Code | Code editor | code.visualstudio.com |
| PlatformIO | Firmware build & upload | VS Code Extension |
| PHP 7.4+ | Web server backend | php.net |
# Clone the repository
git clone https://github.com/Scayar/BinaryLoop.git
cd BinaryLoop
# Open Unified_ESP32 folder in VS Code with PlatformIO
# Connect ESP32 via USB
# Click the Upload button (→) in PlatformIO toolbarPlatformIO will automatically download the required libraries:
Adafruit GFX Library @ ^1.11.9Adafruit SSD1306 @ ^2.5.9
Option A — PHP Built-in Server (Recommended for testing)
cd WebApp
php -S localhost:8000Then open http://localhost:8000 in your browser.
Option B — XAMPP / WAMP (Windows)
# Copy the WebApp folder into your htdocs directory
# Example: C:\xampp\htdocs\binaryloop\
# Start Apache from the XAMPP Control Panel
# Open: http://localhost/binaryloopOption C — Any PHP Web Host
Upload the WebApp folder to your web hosting. Ensure PHP 7.4+ is available and leaderboard.json has write permissions (chmod 666).
- The ESP32 OLED should display "BINARY LOOP" with "PRESS BLUE" blinking
- The web dashboard should load with the cyberpunk UI at your chosen URL
- Try submitting a test score to verify the API connection
stateDiagram-v2
[*] --> Idle: Power On
Idle --> Playing: 🟦 Press BLUE
Playing --> LevelIntro: Start Level
LevelIntro --> Solving: Show KEY & CIPHER
state Solving {
[*] --> WaitInput
WaitInput --> FlipBit: 🟦 Press BLUE
FlipBit --> CheckAnswer
CheckAnswer --> WaitInput: Wrong
CheckAnswer --> Correct: Match!
}
Solving --> NextLevel: ✅ Correct
Solving --> GameOver: 🟥 Press RED (Give Up)
NextLevel --> LevelIntro: Level ≤ 30
NextLevel --> GameOver: Level > 30
GameOver --> ShowToken: Display Score & Token
ShowToken --> Idle: 🟦 Press BLUE (New Game)
| Button | Idle Screen | During Puzzle | Game Over |
|---|---|---|---|
| 🟦 BLUE | Start game | Flip bit at cursor | Start new game |
| 🟥 RED | — | Give up, end game | — |
- Start — Press the blue button on the idle screen
- Solve — Each level shows a
KEYandCIPHERin binary. You must producePLAIN = CIPHER XOR KEY - Cursor — Moves automatically every second across the bit positions
- Flip — Press blue when the cursor is on the bit you want to toggle (0→1 or 1→0)
- Win — When your input matches the XOR result, the screen flashes and you advance
- Give Up — Press red at any time to end the game early
- Token — After the game ends, a 4-digit verification token is displayed
- Submit — Scan the QR code, enter your name and token on the web dashboard
| Levels | Key (Binary) | Key (Decimal) | Bit Width |
|---|---|---|---|
| 1–5 | 0011 |
3 | 4 bits |
| 6–10 | 0101 |
5 | 5 bits |
| 11–15 | 0111 |
7 | 5 bits |
| 16–20 | 1001 |
9 | 6 bits |
| 21–25 | 1011 |
11 | 7 bits |
| 26–30 | 1101 |
13 | 8 bits |
Score per level = Level Number × 100
Maximum possible score = Σ(n × 100) for n=1..30 = 46,500 points
The token system creates a secure, air-gapped bridge between the offline hardware game and the online leaderboard, without requiring WiFi on the ESP32.
sequenceDiagram
participant P as Player
participant E as ESP32
participant W as Web Browser
participant S as PHP Server
P->>E: Plays game (Levels 1-30)
E->>E: Calculate Token = (Score × 7919 + Level × 123) % 10000
E->>P: Display 4-digit token on OLED
P->>W: Scans QR → Opens web dashboard
P->>W: Enters name + token
W->>S: POST /api.php?action=submit
S->>S: Brute-force reverse lookup
Note over S: Scan Score 0→150,000 (step 50)<br>Scan Level 1→31<br>Check: (Score × 7919 + Level × 123) % 10000 == Token?
alt Token Valid
S->>W: ✅ Verified! Score: X, Rank: #Y
W->>P: Show success + redirect to leaderboard
else Token Invalid
S->>W: ❌ Invalid token, Score = 0
W->>P: Show error message
end
SALT = 7919 (prime number)
Token = (Score × SALT + Level × 123) % 10000
Example:
Score = 3000, Level = 10
Token = (3000 × 7919 + 10 × 123) % 10000
= (23,757,000 + 1,230) % 10000
= 23,758,230 % 10000
= 8230
| Protection | Description |
|---|---|
| Token Uniqueness | Each game session produces a different token based on score and level |
| Duplicate Detection | A token can only be submitted once; reuse is blocked |
| Score Validation | Invalid tokens result in a score of 0 (unverified) |
| Brute-Force Verification | Server scans all possible score/level combinations — no shortcut |
| Input Sanitization | All user inputs are validated and sanitized server-side |
Base URL: api.php
Returns the full leaderboard sorted by score (descending).
Response:
[
{
"name": "Player1",
"class": "10-A [L30]",
"score": 46500,
"token": "1234",
"verified": true,
"date": "2026-01-15T10:30:00+00:00"
}
]Submit a score with token verification.
Request Body:
{
"name": "Player1",
"classUnit": "10-A",
"token": "1234"
}Success Response:
{
"success": true,
"rank": 1,
"score": 46500,
"level": 30,
"verified": true,
"message": "Verification successful!"
}Error Response:
{
"success": false,
"message": "Token already used!"
}Returns aggregate statistics.
Response:
{
"totalPlayers": 42,
"verifiedPlayers": 38,
"topScore": 46500
}BinaryLoop/
├── assets/ # Media (e.g. gameplay.mp4 for README demo)
├── Unified_ESP32/ # ESP32 Firmware
│ ├── platformio.ini # PlatformIO configuration & dependencies
│ ├── .gitignore
│ └── src/
│ └── main.cpp # Game engine, OLED graphics, token generation
│
├── WebApp/ # Web Dashboard
│ ├── api.php # REST API (leaderboard, submit, stats, token verification)
│ ├── index.php # Entry point (redirects to public/)
│ ├── leaderboard.json # Score database (auto-created, gitignored)
│ └── public/
│ ├── index.html # Single-page app with cyberpunk UI
│ ├── script.js # Frontend logic (API calls, form handling, navigation)
│ └── style.css # Cyberpunk theme (neon green, CRT scanlines, glitch effects)
│
├── .gitignore
├── CONTRIBUTING.md # Contribution guidelines
├── LICENSE # MIT License
└── readme.md # This file
OLED display not working
- Verify wiring: SDA → GPIO 2, SCL → GPIO 15
- Confirm I2C address is
0x3C(most SSD1306 modules use this) - Ensure 3.3V power (not 5V)
- Check Serial Monitor at 115200 baud for "OLED FAIL!" message
Buttons not responding
- Buttons must connect between the GPIO pin and GND
- No external pull-up resistors needed (firmware uses
INPUT_PULLUP) - Check Serial Monitor — button presses are logged
- Ensure debounce time (120ms) isn't causing missed presses
Token verification fails
- Each game session generates a unique token — restarting the game creates a new one
- The token must be exactly 4 numeric digits
- Each token can only be used once (anti-cheat)
- If the student restarted the game, the old token is invalid
Web dashboard shows "CONNECTION FAILED"
- Ensure the PHP server is running (
php -S localhost:8000in theWebAppfolder) - Check that PHP 7.4+ is installed:
php -v - Verify
api.phpis accessible at the correct URL - Check browser console (F12) for detailed error messages
Leaderboard not saving scores
leaderboard.jsonmust exist and be writable by the PHP process- On Linux/Mac:
chmod 666 WebApp/leaderboard.json - On shared hosting, check file ownership matches the web server user
- Verify the file contains valid JSON (at minimum:
[])
PlatformIO upload fails
- Ensure ESP32 is connected via USB and the correct port is detected
- Install USB-to-UART drivers (CP2102 or CH340 depending on your board)
- Try holding the BOOT button on the ESP32 while uploading
- Check that no other program (Serial Monitor, etc.) is using the COM port
| Layer | Technology |
|---|---|
| Microcontroller | ESP32 (Xtensa LX6, Dual-Core 240MHz) |
| Display | SSD1306 OLED 128×64 via I2C |
| Firmware | C++ / Arduino Framework / PlatformIO |
| Libraries | Adafruit GFX, Adafruit SSD1306 |
| Backend | PHP 7.4+ |
| Frontend | HTML5, CSS3, Vanilla JavaScript |
| Data Storage | JSON flat file |
| Fonts | Orbitron, Share Tech Mono (Google Fonts) |
Contributions are welcome! Please read CONTRIBUTING.md before submitting a pull request.
This project is licensed under the MIT License — see the LICENSE file for details.
Built by Scayar for Advanced Tech Education
