// OVERVIEW
What is Nova Strike?
Nova Strike is a full-featured arcade space shooter running entirely on a Seeed Xiao ESP32-S3 microcontroller. The game renders a parallax starfield, animated pixel-art sprites, particle explosions, and a multi-phase boss fight on a 128×64 OLED display — all at ~50 FPS. Four capacitive touch sensors or push switches give you responsive, satisfying controls. Sound effects and a startup jingle play through a MAX98357A I²S amplifier.
The entire game — 1,151 lines of C++ — runs in the ESP32's on-chip SRAM with no external storage, SD card, or PSRAM required.
// HARDWARE
Components
MICROCONTROLLER
Seeed Xiao ESP32-S3
Xtensa LX7 @ 240 MHz · 512KB SRAM · 8MB Flash · WiFi+BT
DISPLAY
SSD1306 0.96" OLED
128 × 64 pixels · I²C interface · Monochrome · 3.3V
INPUT
TTP223 × 4
Capacitive touch · HIGH when touched · Or use push switches with 10kΩ pull-down
AUDIO
MAX98357A
I²S mono amplifier · 3.2W @ 4Ω · 5V recommended
// WIRING
Pin Connections
OLED — I²C
| SDA | GPIO5 (D4) |
| SCL | GPIO6 (D5) |
| VCC | 3.3V |
| GND | GND |
TOUCH BUTTONS
| UP | GPIO9 (D10) |
| DOWN | GPIO8 (D9) |
| FIRE | GPIO44 (D7) |
| BOMB | GPIO3 (D2) |
MAX98357A — I²S
| BCLK | GPIO7 (D8) |
| LRC | GPIO4 (D3) |
| DIN | GPIO2 (D1) |
| VIN | 5V (recommended) |
| GND | GND |
NOTES
TTP223 outputs HIGH when touched (default mode A).
For push switches: connect one side to GPIO, other to 3.3V. Add 10kΩ resistor from GPIO to GND.
GPIO8 (D9) used for DOWN — GPIO2 (D1) reserved for speaker I²S DIN.
|
// CONTROLS
Button Layout
UP
Move ship upward. Hold for continuous movement. Speed boost active when V power-up collected.
DOWN
Move ship downward. Same speed as UP. Symmetric movement range covers full screen height minus HUD.
FIRE
Single shot on press. Hold for rapid fire (every 4 frames). TriShot power-up adds two diagonal spread bullets.
BOMB
Smart bomb — expanding ring clears all enemies. Deals 3 HP to boss. Max 5 bombs. Starts with 3.
// GAMEPLAY
Core Mechanics
Your ship is fixed on the left side of the screen. Enemies scroll in from the right. Shoot them before they pass or collide with you. Every 10 kills advances the wave — enemies get faster, spawn faster, and become more aggressive. Every third wave spawns a boss.
Lives
Start with 3 lives. Lose one on enemy contact or boss bullet hit (unless shielded). 120-frame invincibility after each hit. Game over at 0 lives.
Scoring
Drifter: 10pts · Hunter: 20pts · Kamikaze: 30pts · Boss: 500 + wave×100. High score persists for session.
Idle Timeout
On title and score screens, a countdown bar appears after 2s. After 6s idle the game auto-returns to title with a hyperspace animation.
// ENEMIES
Enemy Types
DRIFTER
10 PTS · 1 HP
Moves straight across the screen with a slight vertical drift. First wave introduces only Drifters. Easy but abundant.
HUNTER
20 PTS · 2 HP
Slowly homes in on your Y position. Takes two hits to destroy. Appears from wave 2 onwards. Relentless.
KAMIKAZE
30 PTS · 1 HP
Hovers menacingly for 40 frames — then dashes directly at your ship at 2.2× speed. Flashes as warning before dash. Appears wave 4+.
13×9 pixel sprite · Appears every 3 waves · HP scales with wave number (8 + wave×4)
CURRENT HP
ATTACKS
Fires 3 simultaneous bullets at set intervals. Interval shortens each wave.
REWARDS
500 + wave × 100 points
BOMB DAMAGE
3 HP per smart bomb
// POWER-UPS
Power-Up Items
Power-ups have a 20% drop chance on enemy death. They scroll left across the screen; fly into one to collect. Each is marked with a single letter on a small bordered box.
S
Shield
Absorbs one hit. Displays a flashing ring around your ship. Enemy is also destroyed on impact.
T
TriShot
Each fire now launches 3 bullets — one straight, two diagonal. Lasts 400 frames (~8 seconds).
V
SpeedBoost
1.7× movement speed. Stars streak faster in the background for visual feedback. Lasts 350 frames.
B
Bomb Refill
+1 smart bomb, capped at 5. Shown as filled circles in the bottom-right HUD area.
ACTIVE POWER-UPS HUD
While a power-up is active, its letter appears in the top-left corner of the screen. S, T, and V can be active simultaneously. Power-ups do not stack — collecting a second TriShot resets its timer.
// WAVE SYSTEM
Wave Progression
Eliminate 10 enemies to complete a wave. Speed, spawn rate, and enemy variety increase each wave. Every 3rd wave triggers a boss fight before the next wave begins.
WAVE 1
Drifters only · Slow spawn · Speed 1.25 · Learn the basics
WAVE 2
Drifters + Hunters · Medium spawn · Speed 1.5
WAVE 3 — BOSS
Titan-Class Warship · 20 HP · 3-bullet salvo every 55 frames
WAVE 4
All enemy types · Fast spawn · Speed 2.0 · Kamikazes debut
WAVE 5
All types · Very fast · Speed 2.25 · Tight gaps
WAVE 6 — BOSS
Titan-Class · 32 HP · Salvo every 40 frames · Faster movement
WAVE 7+
Speed continues scaling · Spawn rate floor at 15 frames · Boss every 3 waves forever
// HEADS-UP DISPLAY
Screen Layout
TOP-LEFT
Active power-up letters (S, T, V). Hidden when no power-up is active.
TOP-CENTER
Current wave number. Shown as "W1", "W2", etc.
TOP-RIGHT
Boss HP bar — only visible during boss encounter.
BOTTOM-LEFT
Lives remaining as small triangle ship icons.
BOTTOM-CENTER
Current score.
BOTTOM-RIGHT
Bomb count as filled circles. Dims when depleted.
// VISUAL STATES
Animations & Screens
Boot
Scanline fill → "NOVA" flash → triple invert → "STRIKE!" title burn
Title
Ship flies across screen · sine-wave underline · idle countdown bar
Wave Clear
8-arm radial burst from screen center
Boss Entry
Flashing "! BOSS !" warning · boss slides in from right
Death
12-particle explosion + expanding twin shockwave rings
Score
Star medal · score count-up · new record banner pulses
Hyperspace
Stars streak horizontally · title rebuilds letter-by-letter
Smart Bomb
Expanding double ring blast from ship position
// AUDIO
Sound Effects
All audio is synthesized in real-time using sine waves with attack/release envelopes. A non-blocking tone queue prevents sound from stalling the game loop.
SFX_JINGLE
C5 → E5 → G5 → C6 ascending arpeggio. Plays on boot and game start.
SFX_SHOOT
Short 880 Hz blip at low volume. Triggers on every bullet fired.
SFX_HIT
220 Hz thud. Plays when a bullet connects with an enemy.
SFX_EXPLODE
Two-tone descent. Enemy destruction and player death.
SFX_BOMB
Three descending tones. Deep, satisfying. Smart bomb deploy.
SFX_BOSS_DIE
5-note descending cascade. Only plays when the boss is defeated.
// INSTALLATION
Setup Guide
01
Install Arduino IDE + ESP32 Core
Add the Espressif board URL to Boards Manager: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json — then install "esp32 by Espressif Systems".
02
Install required libraries
Open Library Manager (Ctrl+Shift+I) and install: Adafruit SSD1306 and Adafruit GFX Library. Accept dependency installs.
03
Select board
Tools → Board → ESP32 Arduino → XIAO_ESP32S3. Leave all other settings at default.
04
Wire the hardware
Follow the wiring table above. The I²S speaker DIN is on GPIO2 (D1) and DOWN button is on GPIO8 (D9) — no conflicts.
05
Upload
Open NovaStrike.ino, select your COM port, and click Upload. The boot animation will play immediately after flashing completes.
Troubleshooting
OLED blank — check SDA/SCL pins, confirm I²C address is 0x3C with a scanner sketch
No sound — confirm 5V on VIN, check BCLK/LRC/DIN orientation, test with a sine wave sketch
Buttons not responding — TTP223 needs a ground connection; confirm OUT goes HIGH when touched with Serial.println
Compile errors — ensure ESP32 core version ≥ 2.x; driver/i2s.h is included in the ESP32 Arduino core, not standalone
// CUSTOMIZATION
Tuning Constants
All game-feel parameters are defined as macros near the top of the file. Easy to tweak without touching game logic.
// ── Physics ──
#define SHIP_SPD 2 // pixels per frame
#define BULLET_SPD 5 // pixels per frame
#define PIPE_GAP_HALF 11 // enemy gap half-height
// ── Difficulty ──
#define KILLS_PER_WAVE 10 // kills to advance wave
#define MAX_BULLETS 8 // bullet pool size
#define MAX_ENEMIES 10 // enemy pool size
// ── Timing ──
#define IDLE_MS 6000 // idle timeout milliseconds
// ── Power-up durations (frames) ──
ship.shieldTimer = 300;
ship.triShotTimer = 400;
ship.speedTimer = 350;