Show HN: KiDoom – Running DOOM on PCB Traces (mikeayles.com)

362 points by mikeayles ↗ HN
I got DOOM running in KiCad by rendering it with PCB traces and footprints instead of pixels.

Walls are rendered as PCB_TRACK traces, and entities (enemies, items, player) are actual component footprints - SOT-23 for small items, SOIC-8 for decorations, QFP-64 for enemies and the player.

How I did it:

Started by patching DOOM's source code to extract vector data directly from the engine. Instead of trying to render 64,000 pixels (which would be impossibly slow), I grab the geometry DOOM already calculates internally - the drawsegs[] array for walls and vissprites[] for entities.

Added a field to the vissprite_t structure to capture entity types (MT_SHOTGUY, MT_PLAYER, etc.) during R_ProjectSprite(). This lets me map 150+ entity types to appropriate footprint categories.

The DOOM engine sends this vector data over a Unix socket to a Python plugin running in KiCad. The plugin pre-allocates pools of traces and footprints at startup, then just updates their positions each frame instead of creating/destroying objects. Calls pcbnew.Refresh() to update the display.

Runs at 10-25 FPS depending on hardware. The bottleneck is KiCad's refresh, not DOOM or the data transfer.

Also renders to an SDL window (for actual gameplay) and a Python wireframe window (for debugging), so you get three views running simultaneously.

Follow-up: ScopeDoom

After getting the wireframe renderer working, I wanted to push it somewhere more physical. Oscilloscopes in X-Y mode are vector displays - feed X coordinates to one channel, Y to the other. I didn't have a function generator, so I used my MacBook's headphone jack instead.

The sound card is just a dual-channel DAC at 44.1kHz. Wired 3.5mm jack → 1kΩ resistors → scope CH1 (X) and CH2 (Y). Reused the same vector extraction from KiDoom, but the Python script converts coordinates to ±1V range and streams them as audio samples.

Each wall becomes a wireframe box, the scope traces along each line. With ~7,000 points per frame at 44.1kHz, refresh rate is about 6 Hz - slow enough to be a slideshow, but level geometry is clearly recognizable. A 96kHz audio interface or analog scope would improve it significantly (digital scopes do sample-and-hold instead of continuous beam tracing).

Links:

KiDoom GitHub: https://github.com/MichaelAyles/KiDoom, writeup: https://www.mikeayles.com/#kidoom

ScopeDoom GitHub: https://github.com/MichaelAyles/ScopeDoom, writeup: https://www.mikeayles.com/#scopedoom

25 comments

[ 2.9 ms ] story [ 57.1 ms ] thread
(comment deleted)
Of course I love this. DOOM forever.
One of my to-do-one-day projects is an audio jack display system out of a Microcontroller.

Was never quite sure if I should raw XY it or soft modem so I could decode on a web page on a handy device.

I don’t care how this makes the world a better place, because it just does.
This is just a meme now. Doom running on X. I don't get it but congratulations on your very whimsical accomplishment!
Love ScopeDoom!

KiDoom I don't fully get. The website says "All components connected to a shared net; the PCB could be sent to a fab house (it just wouldn't do anything useful)" but I don't see any of the component pins hooked up in the demo video.

Lol, I jsut started learning KiCAD last week, and I work in at a game dev coworking space, so this is a perfect combination of the two! Nice!
Awesome project, but why is the page a semi-transparent overlay on another page?
Inception version: make it so shooting components degrades the machine it runs on.
Or a "tin whiskers" version, where just playing the game degrades the machine--even when powered off.
Or a "tin whiskers" version, where just playing the game gives your player damage--even when the machine is powered off.
The obvious next step is to play Doom rendered as actual PCBs. Each frame is automatically ordered online and then, when it arrives two weeks later, slotted into a holder whilst the bored player contemplates their life choices.
I initially read the headline to mean they got Doom running on a PCB. I suppose with a large enough board that would be possible.
> to extract vector data directly from the engine

That got me thinking “I wonder if anyone has done this on an oscilloscope” and oddly I can't fine anyone who quite has. That DOOM objects are sprites and not actual 3D objects would limit the fidelity, but the scenery could be rendered at least. There are several examples of managing to use a high-speed scope as a low-res monochrome raster device (scanning like a CRT monitor does and turning the beam on & off as needed).

I did find an example of Quake being done on a scope the way I was imagining: https://www.youtube.com/watch?v=aMli33ornEU - as all objects are actual 3D models in Quake that even manages to give them some presence & shape.

EDIT: then I read the second half of this post and saw ScopeDoom! I'm surprised there are no earlier examples that are easy to find.

Side note, this is using the new KiCad socket APIs in v9 right? What was your experience using those?

I've written my own s-expr library to inject footprints and symbols and it's a huge pain and flakey. I'd love to move to something a bit more fleshed out and official.

As an extension, allow new maps to be added based on chip design CAD files - chips become rooms, soldering lines are corridors
Could you print the designs to paper and make a repeatable flip-card stack? Oh no. How long before it becomes a PowerPoint presentation?!
This is one of those projects where the implementation is more interesting than the meme. Rendering DOOM isn’t the impressive part, hijacking a PCB editor’s rendering pipeline and making it behave like a real-time vector engine is.

The part I love most is how many unrelated systems had to cooperate:

extracting geometry directly from DOOM’s drawsegs/vissprite internals

mapping sprite classes to physical component footprints

running real-time updates through KiCad’s object model without triggering full recompute

and then running the same vector stream to an oscilloscope via audio DAC

That’s a really clever chain of “use the tool for something it was never designed to do.”

ScopeDoom might end up being the more interesting long-term direction, vector displays force you to think about rendering differently, and there’s something poetic about DOOM being rendered as literal analog voltage traces.

If you ever take it further, the combination of:

faster DAC (or multi-kHz arbitrary waveform generator)

true analog persistence phosphor scope

and dynamic sprite simplification

…could get you surprisingly close to a smooth vector-shooter aesthetic.

Either way: great hack. The world needs more playful abuse of serious tools.

Just looking quickly but it looks like I have to try to run this on my analog scope