A pseudo-3D kart racing game built for the ESP32-S3 Feather with a 128×64 SSD1306 OLED display. Tilt the breadboard to steer, push the joystick to gas/brake, drift through corners for a speed boost, grab items, and race to the finish line before the AI rival.
Built for CSE 493F Physical Computing (Sp'26), Assignment 2.
(Add link to your Google Drive video here once recorded)
- 3 tracks — Speedway (easy), Twisty (medium), Hairpin (hard)
- 3 characters with stat tradeoffs:
- Balanced — average across the board
- Speedy — high top speed, weaker acceleration and handling
- Drifter — drift specialist with faster charge and longer boost
- Drift mechanic — counter-steer (tilt + opposite joystick X) to drift through corners; release to fire a speed boost
- Items — boxes on the road grant random pickups (boost, shield, slow-mo); use them with the joystick click
- AI opponent — beatable rival kart that slows on tight curves
- State machine — track select → character select → countdown → playing → finished/crashed
| Action | Input |
|---|---|
| Steer | Tilt breadboard left/right |
| Gas | Joystick up |
| Brake | Joystick down |
| Drift | Joystick X opposite to tilt direction while turning |
| Use item | Joystick click |
| Menu navigation | Tilt up/down to scroll, click to confirm |
| Component | Pin / Bus |
|---|---|
| ESP32-S3 Feather | — |
| SSD1306 128×64 OLED | I²C (default SDA/SCL), addr 0x3D |
| ADXL343 accelerometer | I²C (shared bus) |
| Adafruit thumbstick (X) | A0 |
| Adafruit thumbstick (Y) | A1 |
| Joystick click button | GPIO 5 (with INPUT_PULLUP) |
| NeoPixel strip (8 LEDs) | GPIO 6 |
| Passive piezo buzzer | GPIO 10 |
| Coin vibration motor | GPIO 11 (via 2N2222 transistor + 1kΩ + 1N4148 diode) |
Coin motors cannot be driven directly from a GPIO pin. The motor is wired via a low-side NPN switch with a flyback diode:
3.3V ─── motor (red) ─┬─── motor (blue) ─── collector (2N2222)
│ │
diode 1N4148 emitter ── GND
(stripe → 3.3V side) │
base ── 1kΩ ── GPIO 11
Install via the Arduino Library Manager:
Adafruit GFX LibraryAdafruit SSD1306Adafruit NeoPixelAdafruit Unified SensorAdafruit ADXL343
Board package: esp32 by Espressif Systems (Boards Manager). Select Adafruit Feather ESP32-S3 under Tools → Board.
- Wire the components as listed above.
- Open
KartRacer.inoin the Arduino IDE. - Tools → Board → Adafruit Feather ESP32-S3
- Tools → Port → (select the USB port for your Feather)
- Click Upload.
If upload fails because the port is busy, close any open Serial Monitor windows. If the port doesn't appear, hold BOOT, tap RESET, release BOOT to force the board into download mode.
Each row of the screen below the horizon represents a Z distance ahead of the camera. The road is drawn as a stack of trapezoidal segments projected with the math:
scale = CAMERA_DEPTH / zDist
width = scale * ROAD_WIDTH
Closer segments (smaller zDist) draw wider and lower on screen, so
the road appears to recede toward the horizon. Curves are accumulated
along the look-ahead so the road bends visibly as you approach a
corner.
Sprites (trees, rocks, etc.) are placed at fixed Z positions with a side offset and projected the same way — they grow as you approach them. Sprite type is chosen per-track to give each track a distinct visual identity.
A drift engages when:
- The player is turning (tilt past deadzone)
- The player is counter-steering (joystick X opposite tilt)
- The player is moving fast enough (>50% top speed)
While drifting, charge accumulates per frame. Releasing the drift fires a boost whose strength scales with charge held. Drift charges faster for the Drifter character.
A simple speed-following AI: cruises at a target speed, scaled down on tight curves (curve magnitude ≥ 3 → ×0.85, ≥ 4 → ×0.85 again). Smooth acceleration toward the target. No collision or pathfinding — just honest pace.
Original concept, design, and implementation by the author.
AI assistance: Claude (Anthropic) was used iteratively for code review, debugging compile errors, generating boilerplate (sprite drawing helpers, render loop scaffolding), and explaining hardware concepts (transistor driver circuits, flyback diodes, accelerometer tuning). All design decisions, hardware integration, iteration on game feel, and final code review were done by the author.
External references:
- Pseudo-3D road projection technique inspired by Jake Gordon's "How To Build A JavaScript Racer"
- Initial OLED setup pattern based on the BallBounce demo from Makeability Lab
Code released under the MIT License (see LICENSE).