NetHack in Factorio
Play the classic roguelike NetHack inside Factorio.
Real NetHack 3.6.7 C code compiled to WebAssembly, interpreted by a pure Lua WASM interpreter running inside Factorio's modding sandbox. The Factorio game world is the display - every tile rendered as a Factorio entity with real NetHack pixel art. The NetHack world advances one tick when the player moves (or does an action).
This is not an official NetHack port.

How it works
NetHack C source
↓ clang --target=wasm32-wasi
nethack.wasm (1.8 MB)
↓ embedded as Lua data
Pure Lua WASM interpreter (in Factorio's Lua 5.2 sandbox)
↓ imported functions bridge WASM ↔ Factorio
Factorio entities + GUI = the game display
The entire NetHack game logic runs unmodified. Dungeon generation, combat, items, pets, shops, the whole thing. The interpreter executes ~475K WASM instructions per second, with an AOT compiler that boosts hot loops to ~2.8M inst/sec.
The resumable state machine
Factorio's modding API is heavily restricted to enforce determinism for replays and multiplayer lockstep:
- No coroutines (disabled - they are hard to serialize)
- No threads
- No wall clocks or timers
on_tickhandlers must return quickly (<16ms)
But NetHack is a traditional blocking C program: it calls nhgetch() and waits for input.
The WASM interpreter solves this by running as a resumable state machine. It executes a budget of instructions per tick, then saves its entire execution state (program counter, call stack, locals, operand stack) and yields back to Factorio. When the player provides input, execution resumes exactly where it left off.
Some WASM host imports are blocking (like nhgetch - wait for a keypress, or select_menu - wait for menu selection) and some are non-blocking (like print_glyph - draw a tile, or putstr - display a message). When the interpreter hits a blocking import, it pauses execution and sets up the appropriate GUI. The player's next input feeds back through the bridge, the return value is pushed onto the WASM stack, and interpretation continues.
This means NetHack's deeply nested C call stack (e.g. allmain → moveloop → rhack → dokick → kick_monster → ...) stays frozen in the interpreter's state between ticks, without any OS-level threading or Lua coroutines.
Without wall clocks, the instruction budget per tick is a fixed constant - it can't adapt based on how long execution actually took. Too high and the game stutters; too low and NetHack feels sluggish. The AOT compiler and peephole optimizer exist largely to make each budgeted instruction cheaper, squeezing more NetHack progress into the same fixed tick budget.
Features
- Full NetHack 3.6.7 gameplay
-
Character selection dialog

-
UI layout based on the original Qt GUI layout

-
Original NetHack tile art (monsters, objects, dungeon features), plus switchable ASCII Mode. The default has the Factorio Engineer running around the NetHack pixelart world.

- Factoriopedia integration

- Interactive paperdoll (click to equip/unequip/wield/swap)

- Synchronization with Factorio inventory

- Save/load persisted through Factorio's save system - WASM interpreter state is rehydrated on factorio load.
- + export nethack save file function - only works for 32-bit NetHack builds sadly
- No guarantees though. Don't sue me if you lose your save game.
The WASM Interpreter
The heart of the project is a complete WebAssembly interpreter written in pure Lua 5.2, running inside Factorio's sandboxed modding environment. It supports:
- Multi-value extension — blocks with parameters and multiple return values
- Exception handling —
try_table/throwfor NetHack's setjmp/longjmp - WASI preview1 — virtual filesystem, clock, args, preopened directories
- AOT compiler — hot functions compiled to Lua source via
load()for 2-4x speedup - Peephole optimizer — 30+ fused instruction patterns (covering 25+% of function calls, found by profiling NetHack)
- Full spec compliance — all 9,944 WebAssembly spec tests pass
How the display works
NetHack's print_glyph() calls pass through the C window port as WASM host imports, which the Lua bridge translates into Factorio entity creation. Each map cell becomes a Factorio entity with its sprite selected from pre-generated sprite sheets containing all 1,500+ NetHack tiles (monsters, objects, and dungeon features).
The GUI (status lines, messages, menus, inventory) is built with Factorio's native GUI framework, updated by the same bridge imports.