Sometimes you stare at the mountain of dev boards on your desk and realise that none of them actually play Tetris. This is how bad ideas are born. I could have grabbed a phone, installed one of the thousand clones, and moved on with my life. Or pulled out my old Game Boy. Or slotted the cartridge into the Analogue Pocket.
No. Instead I pulled an ESP32 from the drawer, rummaged for a joystick that still had most of its wires attached, and decided to exorcise whatever retro itch was gnawing at me. The result is a handheld-ish Tetris that lives on a breadboard, held together by Dupont wires, optimism, and the unshakeable belief that chasing falling blocks is a valid use of a weekend.
I hadn’t even started and was already dreading the hardest part: naming it. Half the weekend vanished to that question before I settled on ESPer. Being the forward-looking person that I am, why not create a project that will not only run Tetris but other retro games as well, in the future (TM)? I try to convince myself to believe this useful fiction. At least the name sounds clever.
ESPer Tetris – predicting the next block before the RNG does.
What You’ll Need to Summon it
The hardware list reads like a “what’s left in the toolbox” inventory: an ESP32 devkit, 240×240 IPS TFT shamelessly driven by Bodmer’s TFT_eSPI, a “veteran” joystick module with the thousand-yard stare, two functioning buttons (the code expects three; reality disagreed), and a passive buzzer that currently just vibes. Everything is crammed onto a full-size breadboard because designing a proper PCB would have required organisation, and if this was a YouTube video, this is where the sponsor segue would go. The wiring works, largely because the TFT cooperated, and the joystick only needed some dead-zone bribery to stop it from being controlled via telekinesis.
Why Tetris
Honestly, I have no clue; I don’t even like Tetris that much. But if memory serves, I wanted a small game that could easily be turned into a multiplayer version of itself; perfect for systems programming assignments. I make a point of working through the assignments I hand out so I can gauge their difficulty and answer the arcane questions students inevitably invent. That’s why.
No, that’s not it. Tetris is comfort food with tetrominoes. It doesn’t care about ray-traced shadows or shader pipelines; it just wants clean inputs, predictable randomness, and a board that updates without flicker. After a streak of compiler and VM projects, complex ray tracers and all that jazz, I needed something tactile, something that blinks when I poke it. The ESP32 was overkill, but when has that ever stopped anyone? Besides, nothing says “self-care” quite like spending Saturday night tuning cooldown timers so the blocks don’t drop faster than my social score when I decline a night out.
One of these explanations is probably true; use your extra-sensory perception to decide which.
Controls (Mind over Matter)
The joystick feeds into the ESP’s ADC. Values swing wildly depending on caffeine intake, so I hacked in generous dead zones and axis inversion toggles for when you inevitably mount the stick upside down (ahem). Movement and rotation share a simple cooldown system: you tap, the piece nudges, the game takes a breath before it lets you nudge again. Two buttons handle rotations, while the mythical third button still lives in the Pin Definition section as a promise to future-me. Once the thresholds were tuned, the control scheme felt surprisingly natural… almost enough to forgive the missing hard drop. Not to worry, you can still soft-drop by pushing the joystick down.
Rendering without Tears
The board is a single byte array, tetromino layouts are pre-baked 4×4 masks, and the colour palette is one giant nod to late-80s arcade cabinets (more like a crashed screen of a CPC 464, but that doesn’t sound as poetic). A TFT_eSPI sprite keeps the “next” preview crisp while the main board redraw stays mostly glitch-free. Watching the title screen fade to gameplay is oddly satisfying, especially knowing it’s just a homebrew state machine pretending to be polished software. You could port the rendering layer to another project with minimal grief, which I’ve filed under “future misadventures.”
Hitting the Bottom of the Barrel
Scoring follows the time-honoured 100/300/500/800 scheme, multiplied by the current level because the classics got it right the first time. Line clears trigger a very official board redraw and a short breather so you can admire the carnage before the next piece descends. The random-piece generator is a scrappy little byte RNG that spits values fast enough to keep things interesting (ok, ok, tone it down – this is an ESP32, not an ENIAC). Rotation validation still needs work; there’s no fancy wall kick logic yet, so expect the occasional “why won’t you rotate here, you [expletive]” moment near the well edges. Consider it character-building.
Future Mistakes Already Pencilled In
- Add the third button like a responsible adult and wire it to a hard-drop or pause.
- Teach the buzzer to trill instead of staring into the void.
- Implement proper rotation kicks before someone mails me a copy of the SRS spec highlighted in red.
- Retire the breadboard and give the build a shell that doesn’t fling Dupont wires at the floor every time you sneeze.
- Maybe track high scores in flash, or push them over Wi-Fi.
Admire The Skill of the Player
You can admire my skill in the short clip below. Filmed one-handed, while the other hand plays and keeps the breadboard from achieving spontaneous self-liberation, all at once. True parallelism.
Source Code
The code lives in the ESPer GitHub repo. The README hosts a photo of the breadboard and short video [Full Disclosure: The photo and video are the same as shown on this blog]. It’s a bit like the upper tiers of Who Wants to Be a Millionaire – only the brave attempt the final round, or in this case, replicate the build from the image, but if you want to follow along, bring your own joystick, patience, and the willingness to pretend the third button exists. Be my guest.
TL; DR. It’s Tetris on an ESP32.

Leave a Reply