A Verilog-based 2D platformer game inspired by Celeste, recreated on the Basys3 FPGA board with a PMOD OLED display and Xbox controller input.
Huge shoutout to my amazing teammates for their collaboration and dedication!
- Isaac : UI/UX Designer + Collision Detection
- Hao Zhe : Frontend Engineer (Animations & Aesthetics)
- Mark : Backend Engineer (Movement)
- Neeraj : Backend Engineer (Controller)
- Connect the PMOD OLED to the JB port of the Basys3 board with the bitstream flashed into it.
- Turn on the ESP32, preloaded with Bluepad32 firmware.
- Wirelessly pair your Xbox Controller.
- Press
Yon the controller to start the game!!
| Action | Button |
|---|---|
| Jump | A |
| Dash | X + Analog Stick |
| Move | Left Analog Stick |
| Start/Reset | Y |
- Dash is only usable once in mid-air. Resets when grounded.
- Death triggers explosion and respawn with blinking invulnerability.
- Objective is to reach the end of each level.
Activate by flipping SW[15] ON on the Basys3 board.
| Switch | Action |
|---|---|
| SW[0] | Jump to Level 0 |
| SW[1] | Jump to Level 1 |
| SW[2] | Jump to Level 2 |
| SW[3] | Jump to Level 3 |
| SW[4] | Jump to End Screen |
| SW[14] | Return to Home Screen |
🔁 Switches are one-hot encoded: Use only one switch at a time for smooth transitions.
A procedural effect where each pixel's vertical position is offset over time to create falling rain.
On death, an animated explosion plays followed by a blinking invulnerability phase.
- Normal State: Responsive to movement
- Explosion State: Expanding visual effect
- Blink State: Temporary invulnerability and level transitions
- Modular functions:
is_dead,is_level_done,is_obstructed - 8-directional collision flag system
- Works across all four levels
Smooth fade-in/fade-out using linear RGB565 interpolation.
Example: Fade animation between level 0 and level 1.

- Controller inputs processed by Bluepad32 and transmitted via UART.
- Buttons encoded into a byte and decoded on FPGA side using a Verilog module.
- 7-segment display shows number of deaths.
- Automatically resets in Level 0 (Tutorial).
-
A specialized FSM detects rising edges of the dash button and synchronizes them to a 100Hz clock for consistent animation timing.
-
During a dash, the player’s facing direction (left or right) is tracked and used to control a sweep of LEDs (LED[15:0]) that turn OFF in sequence.
-
Once the dash ends, all LEDs are restored to indicate dash availability. A short pulse extender ensures reliable dash activation and direction tracking.
- 🎨 Pixilart: For sprites & backgrounds
- 🕹️ Bluepad32: For controller connectivity
- 🎮 PICO-8 Celeste: Original 8-bit inspiration



