You feel a wrenching sensation.
Play now at nethack.games
NetHack is a four-decade-old roguelike: 442,901 lines of C and Lua that have accumulated layers of intricate and ingenious gameplay rules. It is famous for permadeath: one little mistake can be fatal, and there is no taking it back. This does make it fun, but it can also be a hard game for newcomers, a game you can only learn by dying, and dying, and dying…
The Teleport edition is designed as a sandbox for learning and exploring the game without permadeath. It is a port of NetHack to JavaScript that runs directly in the browser and plays bit-exactly like the original. But what we’ve added here are some new tools for learning:
^Z. This is an especially
forgiving NetHack: press ^Z and your last move is taken
back, exactly as if it never happened. Press it again to go back
further. You can also rewind to an earlier turn, look at what happened,
and fork a new game from that point — so you can practice a hard spot
instead of losing the whole run to it.Since it has no permadeath, nethack.games will never be a replacement for nethack.alt.org. Here high scores are not authoritative, and savescumming is totally allowed. You can use it to make your own speedrun, to learn how to play, or test a strategy.
Go to nethack.games to play in any browser; there is nothing to install and no account to make. Start a game, share a link to play with someone, and open the companion when you want to look something up.
At the command prompt, ^Z (Ctrl-Z, or Command-Z on a
Mac) undoes your last move. Everything that move did is put back exactly
as it was: the map, the monsters and their turns, your inventory and hit
points, the turn counter, even the random numbers. Press ^Z
again to go back another move, or Ctrl-Shift-Z (Command-Shift-Z) to step
forward again. When there is nothing to undo, ^Z does
nothing. The moves you undo stay available to Ctrl-Shift-Z until you
play something different; then the new move takes their place. To try
something without losing the line you are on, scrub back and fork a new
game from there.
--More--, a yes/no question, a menu,
a direction), ^Z is an ordinary key for that prompt, not
undo.^Z stops it, as any key would. A second
^Z undoes it.^Z from any player takes everyone back together, and a line
says who did it.#undo and #redo do the same from the
extended-command prompt. (With the swap_yz option, the keys
are ^Y and Ctrl-Shift-Y instead.)OPTIONS=!undo;
^Z then behaves as it does in C NetHack (and Command-Z does
nothing).The hamburger menu (☰) also runs a few other programs from the same era:
hack — Jay Fenlason’s Hack (1982)rogue — Michael Toy’s Rogue (1980)basic — BASIC interpreterlogo — Logo turtle graphicsThis project began as an experiment in porting NetHack to JavaScript with a swarm of AI coding agents,as best I know how. I started usure if AI agents could reproduce a half-million-line codebase faithfully, bugs and all, and I wanted to find out.
The work is checked by deterministic replay: record a C NetHack session under a fixed PRNG seed, capturing every random number call, game event, and screen; replay the same keystrokes in JavaScript under the same seed; and compare the three, step for step. Where they diverge, I find the first divergence, read the C, and fix the JavaScript. The engine now matches most of the recorded sessions exactly.
This is my second attempt. An earlier one, Menace, taught me a lot about the codebase and about working with agents, but that attempt failed after several months, because the agents produced a massive codebase infused deeply with buggy ideas that prevented forward progress after a point. Read about my first attempt here.
Teleport also runs as a public contest where you can try a port. Fork teleport-contest and write your own JavaScript port of NetHack, by any method. Every push is scored against public sessions in your own GitHub Actions, and an official judge re-scores all forks against the full public and held-out suite every two hours, updating the leaderboard.
js/modal_guard.js)
enforces C’s single-threaded contract — game code cannot fire during a
modal waitSee docs/DECISIONS.md for the full design rationale.
# Serve locally (no build step needed)
python3 -m http.server 8080
open http://localhost:8080
# Check JS-vs-C parity (three-channel report)
node scripts/pes-report.mjs
# Record a C reference session
cd test/comparison/c-harness && bash setup.sh
python3 run_session.py --seed 42| Document | Contents |
|---|---|
| docs/DECISIONS.md | Design decisions with rationale |
| multiplayer/README.md | Running the multiplayer relay + client |
| docs/SESSION_FORMAT.md | Session file format reference |
| docs/LORE.md | Porting lessons and debugging techniques |
| docs/CODEMATCH.md | Function-by-function C-to-JS porting tracker |
| docs/EVENTS.md | Game event logging system |
| docs/CONVENTIONS.md | C-to-JS translation conventions |
| docs/GAMESTATE.md | Global game state structure |
| docs/MONSTERS.md | Monster system architecture |
| docs/COMBAT.md | Combat system porting notes |
| docs/VISION.md | Vision and line-of-sight system |
| yendorbound/README.md | The Yendorbound Agent: autonomous NetHack play, architecture, and evaluation |
| AGENTS.md | Agent instructions and methodology |
modal_guard.js throws if game code fires during a modal
wait.NetHack is distributed under the NetHack General Public License. This JavaScript port is a derivative work under the same license.