📈 GH Trending Explorer 🎲 Surprise me

← all repos

cedardb / sqldoom

git clone https://github.com/cedardb/sqldoom.git

A purely SQL-based reimplementation of the original 1993 DOOM running on CedarDB

+0
stars, 24h · #30,302
+~0
stars, 7 days · #30,777
+136
stars, 30 days · #3,765
136
★ total
10
forks · 0 open issues

Star history (as recorded here)

2026-10-20 · 136 ★ 2026-10-22 · 136 ★
LanguagePython
LicenseGPL-2.0
Created2026-09-22 (1mo ago)
Last push1mo ago
Watchers2
OwnerOrganization

Links

README excerpt
# SQLDoom
SQLDoom ports the original 1993 Doom's game logic and renderer to SQL and runs it inside CedarDB.
The game loop runs at the original 35 FPS, while the renderer produces the complete 320x200 frame buffer at up to 60 Hz on a run of the mill Laptop.
It talks to the database over the Postgres wire protocol and currently requires CedarDB as it uses `cedarscript` for some functions, but could be easily ported to `plpgsql`.
Python only handles timing, reads the keyboard, and displays the bitmap it gets back. Multiplayer also works.
- Full write up -> 
- Live demo (EU) ->
- Live demo (US) ->
SQLDoom is a successor to DOOMQL [[Github]](https://github.com/cedardb/doomql)
which was just a raycast ASCII approximation of Doom.
This version is a more-or-less complete port of Doom, including WAD loading, BSP tree traversal, and textures.
One of these is the 1993 binary. The other is a SQL query. Can you figure out which is which?
Gibbing 3 soldiers with a rocket launcher
## Constraints
1. It should look like the real Doom.
2. But more importantly, it also should *feel* like the real Doom. The original game is just raw *fun*.
3. The rendering must be purely SQL-based. The only acceptable SQL output is a table or a bitmap encoding exact RGB values for every pixel.
4. The game loop must also be purely SQL-based. It's okay to use user-defined-functions inside the DB, though.
5. The Python client only handles input, drives the game tics and renders the output bitmap.
## Architecture
Python is deliberately boring (Rule 5). It uses `pygame` to drive input and output and
triggers a game tic 35 times a second.
Game logic, game state, and renderer live inside the database.
 [code] 
The two paths are intentionally separate: The game logic is triggered on a fixed loop while the client
can asynchronously request a new frame whenever it wants.
## Importing a WAD
Doom's .wad file format is already relational.
The importer (`wad_loader.py`) is about 1300 lines of Python and imports Doom 1 in about 18 seconds.
It writes the tables `sql/schema.sql` defines from the original WAD, populates static data that lived in
Doom's original executable (`sql/loader/*.sql`) and materializes some intermediate results (`sql/loader/functions/*.sql`). 
## The Game Loop
The original Doom ran on a fixed 35 Hz clock, so a tic has a budget of `1000 ms / 35 Hz = 28.6 ms`.
It also drew exactly one frame per tic, so it was capped at 35 FPS as well.
SQLDoom keeps the game logic at 35 Hz (so all the original constants still work), but decouples the drawing.
The client can query (get it?) for a frame whenever it likes and we interpolate the camera position between tics.
So there are two budgets we have to take care of:
- Running a tic every 28.6 ms (or it will feel just completely wrong)
- Rendering at least 35 frames a second (less is kind of okay, but won't feel smooth)
### The tic sequence
Game tics are inherently procedural. We have a sequence of things we have to do each time we run the tic