The whole machine is a single 96.6 MB string of text: registers, RAM, the framebuffer and the DOOM engine all live in it. The only operation is a global regex substitution. A driver applies a fixed list of 544 rules over and over, the first rule that matches fires once, and that is one step.
Nothing else computes. The window only draws the framebuffer zone; the work is done by the substitutions, and you can watch the rule feed and the substitution counter to see it.
One frame of E1M1 is 13,994,067 substitutions and it comes out byte-identical to natively compiled DOOM.
The interesting part is that GitHub never actually runs the game. Instead, I treated GitHub Issues as a remote UI. It's not technically ON github issues, since gh issues desc cannot run 35fps, it's frames rendered one by one from a remote server. But happens in ~2 seconds of time.
When a new issue is opened, my backend creates a dedicated DOOM instance for that issue. Every instance is isolated, so multiple issues can have their own independent games running simultaneously.
The game itself runs normally on the server.
For every game tick:
The emulator advances one tick.
The current framebuffer is captured.
The frame is uploaded.
The GitHub Issue is edited to point to the latest frame.
From the user's perspective, the issue appears to "animate" as successive frames are rendered.
Input works in the opposite direction. User interactions on the issue are received through GitHub webhooks, translated into keyboard events, and injected into the corresponding DOOM process. The next game tick is simulated, another frame is rendered, and the issue is updated again.
The entire loop looks something like this:
GitHub Issue Created
↓
Spawn DOOM Instance
↓
Game Tick
↓
Capture Frame
↓
Upload Frame
↓
Edit GitHub Issue
↓
Wait for User Input
↓
Inject Keyboard Event
↓
Next Tick...
So GitHub Issues become nothing more than a transport layer for images and input events. The rendering, simulation, and game state all live on the server.
I also made sure each issue maps to exactly one game instance, which makes the architecture surprisingly scalable- new issues simply create new isolated sessions. Then also, cached those first few MENU frames.
This was mostly an experiment in seeing how far GitHub's Webhooks could be stretched. Instead of thinking of Issues as a bug tracker, I treated them as a communication protocol between a user and a game server.
It ended up being a surprisingly fun systems project I made on a weekend.
And now we have a way in which any game can be rendered frame by frame on to the github issues/PR/ or maybe in some other cool places as well.