A pixel-art scene depicts a database-driven shooter game, with SQL tables and code surrounding a first-person corridor encounter.
Somewhere out there, a database engineer has finally answered the question nobody asked in a boardroom: can a SQL engine run Doom? It can. CedarDB developer Lukas Vogel ported both the game logic and the renderer of the original 1993 Doom to SQL, and the result is called SQLDoom. The project runs the port inside CedarDB. The Register covered it on 3 October as a sequel to the author's DOOMQL stunt, which looked more like 3D Monster Maze than the 90s pixel blaster.

It is a demo, not a product. Even so, it shows what a set-oriented query engine can do when game state is stored as relational data. It also shows where "pure SQL" stops.

What was actually built​

Vogel's blog post is dated 22 September 2026. The game loop runs at the original 35 FPS, while the renderer produces the complete 320x200 frame buffer at up to 60 Hz on an ordinary laptop.

The client side is deliberately thin. Python only handles timing, reads the keyboard, and displays the bitmap it gets back. The project's architecture notes say game logic, game state, and renderer live inside the database. So this is not a game where every function runs in SQL. It is a SQL-driven engine with a small Python shell around it.

The two code paths are separate. The game logic ticks on a fixed 35 Hz clock. The renderer is a pure function of the state tables, so the client can ask for a frame whenever it likes.

Why DOOMQL needed a sequel​

The original DOOMQL used raycasting and ASCII output. The GitHub README describes it as a raycast ASCII approximation of Doom. Critics pointed out that this made it closer to Wolfenstein 3D. Vogel told The Register that a Hacker News commenter complained that raycasting was used instead of BSP-tree traversal, which that commenter considered the thing that made Doom revolutionary. He said he "couldn't let that stand."

SQLDoom is the answer. The README calls it a more-or-less complete port of Doom, including WAD loading, BSP tree traversal, and textures. That "more-or-less" matters. It is a faithful port of the core engine, not a claim that every feature of every Doom port is covered.

The ground rules​

Vogel set constraints for himself. The post lists them as follows:

  • It should look and feel like the real Doom.
  • Rendering must be purely SQL-based, and the only acceptable SQL output is a table or a bitmap encoding exact RGB values for every pixel.
  • The game loop must also be purely SQL-based, though user-defined functions inside the database are allowed.
  • A client in another language may only handle input, drive the game tics, and render the output bitmap.

How it works under the hood​

Loading the level data​

Doom's WAD format maps neatly onto tables. Vertices connect to linedefs, which connect to sidedefs and sectors, and sectors contain objects. According to the project documentation, the Python importer is about 1,300 lines and imports Doom 1 in about 18 seconds on Vogel's laptop. The blog post mentions roughly 1,000 lines, so treat "about 1,000 to 1,300" as the safe range.

The game loop​

The original game ran at 35 Hz, which gives each tic a budget of about 28.6 ms. SQLDoom keeps that rate so that the original timing constants still work. It decouples drawing and interpolates the camera position between tics.

Vogel's measurements for the loop are:

  • Typical tic: about 2.15 ms with six monsters awake, roughly 8% of the budget.
  • Worst case found: 10.45 ms on E4M1 with 46 monsters awake, about 37% of the budget.
  • Size: about 5,900 lines of SQL, against roughly 9,000 lines in the original C source, per Vogel.

The tic logic is orchestrated in cedarscript, CedarDB's scripting language. Vogel says it closely resembles PL/pgSQL. He also says writing the logic as set-based UPDATE statements helped the Entity Component System pattern click for him.

The renderer​

Each frame is one large query. According to the project, the renderer is about 1,300 lines of SQL, excluding comments, spread across 89 common table expressions. It ends by aggregating 64,000 pixel rows into a single 192,000-byte RGB value.

Three tricks do most of the work:

  1. BSP ordering without recursion. Root-to-subsector paths are precomputed at load time and packed into an integer. Sorting on that integer yields the front-to-back order, and bounding-box checks cull subtrees that are out of view.
  2. Floors and ceilings via window functions. Doom's mutable clip arrays are an imperative algorithm. Vogel emulates them by ordering wall panels per screen column and computing the clip state from the preceding rows.
  3. Depth resolution with MIN(). Walls, planes and sprites emit candidate pixels. A packed key puts depth in the most significant bits, so a MIN() aggregate picks the nearest candidate and carries the colour payload with it.

By Vogel's own account, the third step is the costliest: depth resolution averages 8.2 ms, more than a third of the frame. Floors, ceilings and sky cost about 3 ms, and walls about 1.7 ms. On his Ryzen 7 PRO 7840U laptop he typically sees about 60 FPS, dropping to 35 FPS in very busy scenes. These are the author's numbers, not independent benchmarks. The post also offers no head-to-head comparison against another database.

Deathmatch and the "free" multiplayer server​

Multiplayer is the part where a database feels like a natural fit. The blog lets readers play now, with deathmatch, four slots, first come first served. It runs the shareware version of the first episode, and if all seats are taken you land in a queue. If the queue is full, you can still query live game state via SQL while you wait.

Vogel argues a database gives you several things that game developers normally build themselves:

  • authentication and access control
  • consistent snapshots of game state
  • a binary wire protocol
  • atomic game tics, since each tic runs inside a transaction

On access control, the post says the four player roles can only call a few API functions, and the input function clamps values to allowed ranges. The project has about 110 tables and just over 100 functions.

Vogel says the game is perfectly playable on his laptop. The Register found the hosted-server performance a little sluggish, but it suspected the servers rather than the port. That is a reporter's impression, not a measured benchmark. It also doesn't show that databases generally make better game servers.

The CedarDB angle​

The Register is blunt about the commercial side. It calls this a thinly veiled demonstration of CedarDB's capabilities, and that is fair. CedarDB is a compiling database system that lowers complex queries to LLVM IR and then compiles them to machine code.

Vogel also compared generated code for one movement routine. The compiled C version came to 48 instructions and the SQLDoom version to 117. He notes that 42 of the extra instructions store results back into a table, which C doesn't have to do. That is one author's single, non-like-for-like sample, but it is an interesting hint at how query compilation behaves.

Limits and caveats​

  • CedarDB is required for now. The README says it uses the Postgres wire protocol but needs CedarDB because it uses cedarscript for some functions, though it could be ported to PL/pgSQL. Running on stock PostgreSQL has not been demonstrated.
  • Game data isn't included. Vogel says he can't supply a Doom IWAD. The shareware doom1.wad is freely redistributable and enough for episode 1, and retail WADs work if you own them.
  • Testing is informal. The project documents smoke tests across all maps, deterministic replay checks and multiplayer access-control checks, and says there are no formal unit tests.
  • It's not a recommendation. Vogel himself calls rendering Doom in a database "obviously a bad idea" while defending parts of the design.

Trying it yourself​

Vogel lists three requirements: CedarDB Community Edition, Python with psycopg2 and pygame, and a Doom IWAD. After that, follow the repository README. The code is GPL-2.0, per the GitHub listing, which also shows 37 stars at the time of capture.

The takeaway​

This is a software-development curiosity with a real lesson inside. Relational thinking maps surprisingly well onto game state and logic, while a set-based engine strains at the imperative parts of a renderer. The result is a faithful Doom in about 5,900 lines of SQL for the logic and 1,300 for the renderer, plus a thin Python client. Nobody should ship a game this way, but it is a charming stress test for a query engine, and it will probably keep a few DBAs happily distracted for a weekend.

 

References

  1. GitHub - cedardb/sqldoom: A purely SQL-based reimplementation of the original 1993 DOOM running on CedarDB · GitHub github.com
  2. Clever database, but can it run Doom? The Register 2026-10-03T12:02:00+00:00
  3. Building a DOOM-like multiplayer shooter in pure SQL | CedarDB cedardb.com