libd2: Diablo II's Map Generator Rebuilt Cell for Cell in Zig

By Indie Kings | November 4, 2026

Updated November 4, 2026: libd2 is a clean-room reimplementation of the Diablo II 1.14d engine core in Zig, reverse-engineered from the retail binary with no Blizzard code. It began with the seed-driven map generator and now covers item generation, the rules the server applies, the D2GS wire protocol, save files and routing across a generated world. The repository was created on July 2, 2026 and holds 25 stars under an MIT-style licence.

Lut Gholein, the Act II town from Diablo II, rendered in the game's journal-map art

Image: The Act II town Lut Gholein, the kind of in-game geography libd2 reproduces. Credit: Wikimedia Commons.

Per the official repository README, libd2 is a clean-room reimplementation of the Diablo II 1.14d engine core in Zig, reverse-engineered from the retail binary with no Blizzard code. It began with the seed-driven map generator, where you give it a seed and it produces the same world the game does, and it now covers the surrounding systems including item generation, the rules the server applies, the D2GS wire protocol, save files, and routing over a generated world. Each package stands on its own.

The clean-room claim is the part worth understanding, because it is doing legal work as well as architectural work. A clean-room reimplementation means the code was written from observation of behaviour and from the binary, not copied from leaked sources or from the original's own code. For a game engine core, that is the difference between a project that can exist publicly and one that cannot. The README states it in the first sentence rather than burying it.

A clean room, and what that buys

Writing the core in Zig is a deliberate choice and the README links a separate document explaining it. The practical reason is visible in the output: a package can be compiled to native shared and static libraries with a plain C header, and to a freestanding WebAssembly build, from the same source. That is what makes the multi-language story possible without a binding layer on either side, because each language calls the same exported symbols rather than a wrapper around them.

Four packages ship with that C ABI today: map generation, item generation, pathfinding and networking. A utility package also exports a C ABI for palette-index upscaling, for linking into a renderer, though it is not in the WebAssembly build or the language bindings. The README does not overstate coverage. It names exactly which packages have the portable surface and which do not.

The architectural payoff is worth spelling out. A server operator who needs item generation, or a tool author who needs a seed-accurate map, does not have to run the game, run a private server, or read a decompilation. They call a library. That is a meaningful shift for a community that has historically worked by reverse engineering the binary or by reading other people's source.

Map generation checked cell for cell

The verification section is the strongest evidence in the repository, and the README presents it plainly. Map generation is compared cell for cell against dumps captured from the retail engine: 11.1 million subtiles per seed, every level of all five acts, zero differing cells. Two of the seeds are blind holdouts, captured up front and never looked at while developing the generator.

ClaimEvidence per the README
Map generation matches the gameCell for cell against retail dumps, 11.1 million subtiles per seed, all five acts, zero differing cells
Overfitting is ruled outTwo of the seeds are blind holdouts, captured up front and never examined during development
Difficulty coveragePer-cell captures are all Nightmare. Normal and Hell are checked per level, not per cell
Other packagesThe README says the remaining packages rest on weaker evidence and points to a verification document

Two of those rows are more valuable than the headline. Blind holdout seeds are the standard defence against a generator that was tuned until it fitted its test data, and the README says the seeds were captured before development and never looked at. The difficulty caveat is the honest counterweight: per-cell comparison is Nightmare only, while Normal and Hell are checked at the level rather than the cell. Both statements are in the same section, which is how a reader can judge the strength of the claim instead of taking it on faith.

The README goes further and says the other packages rest on weaker evidence than map generation, pointing readers at a verification document before they depend on one. That sentence is the reason to trust the rest of the page. A project willing to rank its own subsystems by evidence quality is describing a measurement programme rather than a feature list.

One core, eight languages

Distribution is already solved in a way most engine reimplementations never get to. The README lists installation for Rust through cargo, .NET through NuGet, and Node, Bun, Deno and the browser through npm, plus Go, Zig, C and C++ with instructions for each. Anything else with a C foreign function interface works as well. The packages with a C ABI compile to native shared and static libraries with a header, and to a freestanding WebAssembly build, so the same artifacts serve every language listed.

The npm package is a single combined WebAssembly module behind a typed TypeScript API, and the README explains why: the subsystems pass pointers to each other, so separate modules would mean separate memories and a copy of every collision grid across the boundary. That is a real engineering constraint stated as one, and it is the kind of detail that tells you the port was designed rather than assembled.

The WebAssembly is freestanding and libc-free, which means its import object is empty and the shim reads it with fetch or the filesystem depending on where it finds itself. That is what lets the same package run in Node, Bun, Deno and the browser without a native addon. The README also notes that older per-subsystem packages are deprecated in favour of the combined one.

You can try it without installing anything

The most immediately useful thing in the README is a live endpoint. A map generation server instance is running, so one HTTP request returns a real act. The parameters are a seed, an act from one to five, and a difficulty from zero to two, and the response contains every level of the act: rooms, preset objects and monsters, level adjacency, and the collision grid.

The README gives a worked example. Act I at seed 1337 is 39 levels and about 230 kilobytes gzipped, generated on the fly in well under a second, with a health endpoint for checking the service. For anyone evaluating whether the project is real, that request is more persuasive than any paragraph, because it either returns a familiar act layout or it does not.

The TypeScript example is equally concrete. You initialise the library, open a game with a seed, and ask for an area, and nothing is generated until an area is asked for, because one generation covers its whole act. The README shows the output for Cold Plains: 97 rooms, four ways out, leading to Cave Level 1, Blood Moor, Burial Grounds and Stony Field. Routing crosses levels in one call rather than one per area, and the printed route goes Rogue Encampment, Blood Moor, Cold Plains. A reader who has played the game can tell immediately whether that shape is right.

Where the evidence is weaker

The repository was created on July 2, 2026, was last pushed on November 2, 2026, and is written in Zig with 25 stars and 7 forks. It carries an MIT-style licence that the GitHub API reports as non-standard, so anyone reusing it should read the licence file rather than the badge. Four releases are published, all from August 22, 2026, tagged 0.6.1 through 0.6.5. Release downloads are zero for the current set, which is unsurprising for a library that is consumed through language package managers instead of release assets.

The honest summary of the project's own claims is this: map generation is proven at a standard most preservation projects never reach, and the surrounding packages are useful with weaker evidence behind them. The README says exactly that and links the document that says so in more detail. Anyone building on the item generation or networking packages should read that document first and decide whether the evidence level suits their planned dependency.

There is a second, quieter caveat. Reverse-engineering a game's engine core sits in a legal grey area that no README can resolve, and this one does not pretend to. It says no Blizzard code is used, it is a clean room, and it is distributed as a library rather than as a game. Those are true statements about method and packaging, and a reader who needs more than that should ask a lawyer rather than a README.

Our take: cell-for-cell map generation against retail dumps, with blind holdout seeds, is the kind of claim this site exists to surface. It is measurable, it was measured, and the project published where its other packages are weaker instead of quietly blending them together. If you need seed-accurate Diablo II world generation for a server, a tool or a study, libd2 is the library to start from.

FAQ

Is libd2 a Diablo II private server?
No. Per the official repository README, it is a clean-room reimplementation of the Diablo II 1.14d engine core in Zig, covering map generation, item generation, server-side rules, the D2GS wire protocol, save files and routing. Each package stands on its own.

Does it use Blizzard code?
Per the README, it is reverse-engineered from the retail binary with no Blizzard code, and it is described as a clean-room reimplementation.

How accurate is the map generation?
Per the README, map generation is compared cell for cell against dumps captured from the retail engine: 11.1 million subtiles per seed, every level of all five acts, zero differing cells, with two blind holdout seeds. Per-cell captures are Nightmare only; Normal and Hell are checked per level.

Which languages can use it?
Per the README, Rust, .NET, Node, Bun, Deno and the browser get first-class packages, and Go, Zig, C and C++ are supported through the C ABI, along with anything else that has a C foreign function interface.

Can I try it without installing anything?
Yes. Per the README, a live map generation server accepts a seed, act and difficulty and returns every level of the act with rooms, preset objects, monsters, adjacency and the collision grid. Act I at seed 1337 is 39 levels, about 230 kilobytes gzipped.

Is everything as well verified as the map generator?
No. Per the README, the other packages rest on weaker evidence, and readers are pointed at a verification document before they depend on one.

Bottom Line

libd2 is a clean-room Zig reimplementation of the Diablo II 1.14d engine core, and its map generator is verified to a standard that most projects in this space never attempt: cell for cell against retail dumps, 11.1 million subtiles per seed, zero differing cells, on seeds that include blind holdouts. It exposes that core to Rust, .NET, Node, Bun, Deno, Go, Zig, C and C++ through one C ABI and a freestanding WebAssembly build.

The README's own honesty is the reason to trust it. It ranks its packages by evidence strength and points out that the surrounding systems are on weaker ground than the generator. Four releases shipped in August 2026, and the licence should be read rather than assumed. For anyone who needs seed-accurate Diablo II world generation, this is the library to start from.

Related: D2Decomp: Diablo II Rebuilt as a Native Engine, Mod Diablocraft: Diablo 3 Systems Rebuilt on a WotLK AzerothCore Server

Sources: per the official libd2 repository README at github.com/jaenster/libd2, fetched in full on November 4, 2026, including the verification section, the language table and the live example; repository metadata (25 stars, 7 forks, 4 open issues, created July 2, 2026, language Zig, last pushed November 2, 2026) re-verified via the GitHub API on November 4, 2026; release list for v0.6.1 through v0.6.5, all dated August 22, 2026, from the same repository.

Share