Play gPu(doom)

Turlogg · Trail log

TRM Plays gPu(doom)

A tiny recursive model plans every route for a bot playing gPu(doom). It clears the first level. The rest of the episode is where the bot around it fails.

DOOM's fire, on your GPU · click it

On the evening of October 29 Oskar asked whether Samsung's Tiny Recursive Model could drive something in a WebGPU game, and then: "Could we use it to play doom?" By the next morning a bot was clearing E1M1 of gPu(doom) with TRM as its only route planner. By lunchtime it had made 173 attempts on an arena page that Oskar left running, mostly on his Mac. Four of them were E1M1 clears, and none of the others reached an exit. This post covers what the model does, what the bot around it does, and where each one failed.

The start of Freedoom's E1M1 with the pistol raised, rendered by gPu(doom)
The bot on E1M1, planning every route with TRM on your GPU. It needs a browser with WebGPU; on a phone the planning is slow and the bot plans only the area around it.

The model

TRM (Jolicoeur-Martineau, arXiv 2510.04871) solves grid puzzles such as Sudoku, mazes and ARC tasks by applying one small network to its own output over and over, and it reads out an answer after each round. The version here has 820,000 parameters. It is given a grid made from the level around the player, with cells for floor, wall, door, nukage, start and goal, and it marks every cell that lies on a shortest route from start to goal. Breadth-first search (BFS) produced the training labels from 300,000 grids cut out of E1M1–E1M8 with random positions, goals, door states and keys. E1M9 was held out.

The paper's main model mixes cells with self-attention. An answer counts as exact only if all 1,024 cells of a 32×32 grid are right. With attention, exact answers reached 1.7% after 4,000 training steps on a Colab A100. With a 3×3 convolution in place of attention, each step ran four times faster, exact answers passed 27% by step 5,000, and they reached 94.0% by step 60,000. On grids from E1M9 it is exact 93.3% of the time. Fine-tuned on mixed sizes, the same network plans a whole level in one grid of up to 152 cells a side. Inference is written in WGSL and runs on the game's own GPU device, next to the engine's compute kernels. One round on a 32×32 grid takes 12 ms on the Mac.

The bot around it

TRM only answers "which cells are on the route." Everything else is hand-written JavaScript: choosing the next target (a key, the exit, a switch, an item, an unexplored room), turning the predicted cells into steering, opening doors, calling lifts and shooting. The bot plans the whole level with TRM on a coarse grid of 48-unit cells, takes the farthest route cell inside the 32×32 window of 24-unit cells around the player, plans that window with TRM again, and walks it. (A DOOM corridor is typically 64 to 128 units wide.)

In test runs with a pistol start, the bot cleared E1M1 in 3 of 3 runs on the second difficulty (Hey, Not Too Rough) and 2 of 3 on the third (Hurt Me Plenty). Its fastest clear, in the arena, took 24.3 seconds of game time. It cleared no other level.

The arena

Oskar asked for the bot to play somewhere he could watch it, with a log shipped back after every attempt and a timeout when it got stuck. The DOOM Arena page plays the bot on screen on the visitor's GPU. After each attempt it commits the log and a few thumbnails to a private repository and comments on a pull request, which wakes the Claude session that wrote the bot. An attempt with no progress for 45 seconds, meaning no kills, no items, no new places and no targets reached, ends as "stuck". Each level gets two tries with different random seeds, which change what the monsters do.

Oskar ran it on the Mac through the morning and on an iPhone for part of it, until it turned out that the phone's GPU drew more power than its wireless charger supplied. Every stall or death in the logs became a candidate fix, and every fix ran first through the GPU bridge from the previous post, as a same-seed comparison against the version without it. A run is deterministic for a given seed and code, so the difference between the two came from the change.

Failures in the logs

To find out whether the model was the weak part, the bot ran BFS on the same grid every time TRM returned no route to a target. On E1M2, BFS agreed in 204 of 220 cases: the target was really closed off at that moment, behind a door the bot hadn't opened or a lift it hadn't called. The planner was answering correctly, and the bot kept asking it about places it couldn't reach yet.

The other failures were in the bot, and turned up in this order.

  • Lifts. The bot didn't ride them, and E1M2 stopped at the first one. With lift riding it lasted ten minutes there and picked up a key.
  • Thin walls. Each 48-unit cell of the whole-level grid covers four 24-unit cells, and it counted as open when any of the four was. A wall that straddled a coarse boundary disappeared, so routes ran through E1M2's locked blue door and into E1M3's switch room through a corridor wall. After cutting those joins, the share of window plans that failed to connect fell from 63% to 33% on E1M2 and from 81% to 17% on E1M3.
  • Nukage. E1M3 has a nukage pit with two switches inside it. The bot went in for a switch and died there in all three test runs. Once it skipped targets inside damaging pits and kept moving instead of stopping to fight on a damaging floor, two of three runs survived to the five-minute cap.
  • Unreachable targets. A target that failed came back on a timer and failed again. Setting it aside until a switch, a lift or a kill changed the level took E1M2's kills from 11, 11 and 3 to 28, 21 and 8 on the same seeds.
  • A lift switch faced the wrong way. On E1M7 the bot stood 16 units from the switch that calls a lift and pressed use while it faced a monster it couldn't reach. DOOM only activates the switch you face.

On E1M8 the two Barons of Hell never woke in most attempts, and the arena's no-progress timer ended every one of those at exactly 209 seconds. It looked like an engine bug, so a second Claude session checked gPu(doom)'s monster wake-up against DOOM's source. Sight and sound propagation matched. The bot had never come within 1,090 units of either Baron, and it never fired, so no sound reached them. It had spent 40 seconds walking back and forth between two rooms on the way to an exit it couldn't reach, and then stopped exploring. The three stalled runs had identical traces despite three different seeds: the seed only changes what monsters do, and none were awake. With the bot set to explore after an unreachable target, it crossed the map to the Barons in all three test runs. With only a pistol it has killed neither.

Combat

Deaths on E1M4–E1M6 were the other half of the failures, and three changes to how the bot fights went through matched tests there: two seeds per level, scored by total game time survived, each against the unchanged bot on the same seeds. Shooting zombiemen and shotgun guys first, because their hits land instantly, scored 827 seconds against 819. Combined with retreating whenever health fell fast, it scored 782 against 826 (an earlier build's baseline), because retreating kept the bot away from the health pickups it needed. Making health below 70 and armour below 50 worth a long walk scored 1,017 against 819, with one death fewer. That one shipped.

Results so far

In the last full arena session on the Mac, 14 attempts at E1M2–E1M8 ended in six deaths in fights, all on E1M4–E1M6, and eight stalls on E1M2, E1M3, E1M7 and E1M8. When TRM says there is no route, it is right nine times in ten. The bot doesn't know the order of things: which switch opens which door, when to call a lift, which fights to avoid. That would be hand-written code, the part TRM doesn't do.

The code, the training runs and a longer write-up with every comparison are in experiments/trm-doom. The game's ?bot mode runs the same bot.