A network switch that plays Doom
I took a TP-Link TL-SG1024DE — a 24-port gigabit switch with a basic web page for settings — and put the first level of Doom inside its firmware. You log in to the switch's web interface, open one page, and it loads a playable E1M1 in your browser. The game's data comes from the switch itself. No server, no internet, no laptop in between.
The game is a small raycasting engine I wrote from scratch in JavaScript. It reads the real level layout — walls, stairs, doors, lighting, where the enemies stand — and draws it with flat-colour textures, a few enemy types, a pistol, and a square-wave version of the level music, which is as junky as it sounds.
The serial console never talked, and it didn't matter
My first plan was the usual one: find the serial pins on the board, plug in a USB adapter, and get a console. I found the header, traced it to the CPU (a Realtek RTL8382L) with a multimeter, and captured the line with a logic analyzer. I got hours of noise and not one real byte.
I even pulled the baud rate out of the firmware afterwards — the code sets up the UART at about 38400 baud — but the pins never spoke. I'm still not sure why. What actually worked was skipping the console: the firmware lives in a small SPI flash chip next to the CPU, and a cheap clip programmer can read it while the board is unpowered.
No Linux, just a MIPS blob with a web server in it
The 2 MB dump turned out to be big-endian MIPS code for a small real-time system, not Linux. The web interface is stored as plain text right in the flash — I could search for menu labels and find them. Two copies of the main program sit in the chip, one at the start and one in the second half.
The useful discovery was how pages are served. Each one has a little handler function that sends the page by calling an append routine with two numbers baked into the code: where the page is, and how long it is. To replace a page, you don't need to understand the web server at all. You change those two numbers, put the new bytes in some unused flash, and you're done.
Verify by reading it back
Before putting a game on the chip I wanted to prove I could change anything. So I edited one menu label, “System Info” to “Doom Switch,” flashed it, and looked. Nothing changed. The programmer had said it wrote fine. The switch booted fine. The label was still the old one.
I read the chip back and compared checksums. The chip held my original dump, byte for byte. I'd written the wrong file. The second attempt, with a read-back after it, changed the menu label and proved the whole pipeline worked.
E1M1 without most of Doom
Doom's real data is far too big for this flash, and I didn't need most of it. The level itself is small: a few hundred wall segments, heights, light levels, and positions. I converted just that, replaced every texture with its average colour, dropped all the sound and speed-up data, and ended up with about six kilobytes after compression.
- Rendering: for each screen column, the engine fires a ray and tests it against every wall in the level. E1M1 has under 500, so this brute-force approach is plenty fast — no binary space partitioning needed.
- Doors and lifts: doors open and close, and the floor triggers that lower platforms are handled too.
- Enemies: three kinds, each drawn from code instead of artwork, with simple chase-and-shoot behaviour.
- Sound: noise bursts for gunshots and doors, and the famous bass riff as a square wave.
Three help pages and a loader
The finished game is about 22 KB, compresses to about 13 KB, and turns into 17 KB of text once it's encoded to survive being served as a web page. I wasn't sure how big a response the switch's web server would accept, so I split it into three pieces of under 6.5 KB each. They live in the switch's help pages for features a basic switch rarely needs — cable test, loop prevention, port mirroring.
The first of those pages is a tiny loader. It fetches the other two, glues the pieces together, unpacks them in the browser, and replaces itself with the game. The switch only ever hands out three ordinary-looking text pages.
/help/ folder and I'd left that off the address. Switching to relative addresses fixed it for good.What this is and isn't
This is a hack on one old, spare switch that I was happy to risk. It's tested on exactly one unit of this model and hardware revision, so I can't promise anything for another. The method — read the flash, find where pages are stored, swap them — should carry over to similar switches, but the details won't.
I'm not posting the firmware, since it belongs to TP-Link. The playable page does contain E1M1's layout, redrawn with flat colours, and that level comes from id Software's shareware release. It's a fan project with no affiliation. The engine code is mine, and the method is all in this page.