# Miu2D: a from-scratch 2D ARPG engine in TypeScript, Rust and raw WebGL

> Miu2D is a 176,000-line 2D action RPG engine that renders through raw WebGL with no game framework underneath, and it has been used to rebuild three Kingsoft wuxia RPGs in the browser. Here is what it actually contains, how to run it, and where it stops being the right tool.

**luckyyyyy/miu2d** — Miu2D is a 2D RPG game engine built with Rust + TypeScript + React + Canvas, designed for the Web platform. 2/ / .

- Repository: https://github.com/luckyyyyy/miu2d
- Website: https://miu2d.com
- Stars: 474 · Forks: 92
- Language: TypeScript
- License: MIT
- Published: 2026-08-08 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/luckyyyyy-miu2d

## The problem Miu2D solves, and the people it is for

Most browser RPG projects start by picking a framework. Miu2D starts by refusing one. The README states the engine renders through raw WebGL with no dependency on Unity, Godot, Phaser, PixiJS or any other game framework, and that every subsystem, from sprite batching to the scripting VM, is implemented from first principles. The stated audience is narrow: developers who want to see and modify every layer of a 2D action RPG, and anyone rebuilding a classic tile-and-sprite RPG whose data is already in hand.

The proof of concept is unusual. The project says it has been used to rebuild three Kingsoft (西山居) wuxia RPGs, all playable in any modern browser: Legend of Yue Ying (2001), Swords of Legends 2 (1998) and New Swords of Legends (2001). Those are not demos written for the engine. They are commercial games from the late 1990s and early 2000s, which means the engine has to decode their original binary assets and reproduce their scripting behaviour rather than define a clean format of its own.

That framing matters when you evaluate it. Miu2D is not trying to be a general game engine with a friendly scene graph. It is trying to be a faithful runtime for a specific genre and a specific asset lineage, and it accepts the complexity that comes with that.

## How the rendering, pathfinding and scripting layers fit together

The repository splits into eleven pnpm workspace packages, and the README's architecture table names the important ones. @miu2d/game is the UI layer: React 19, three themes (Classic, Modern, Mobile) and 84 components. @miu2d/engine is pure TypeScript, 215 files across 19 modules, with no React dependency, so the simulation does not know the UI exists. @miu2d/engine-wasm holds the Rust code compiled to WebAssembly. @miu2d/server is Hono plus tRPC plus Prisma over PostgreSQL. @miu2d/dashboard is the editor.

The rendering path is where the design shows. A SpriteBatcher coalesces roughly 4,800 map tile draws into 1 to 5 WebGL draw calls, and a RectBatcher reduces about 300 weather particles to a single call. A real-time lighting pass composites per-entity additive glow masks with SHD-based shadow rendering. There is a Canvas2D fallback when WebGL is unavailable, and GLSL filters for poison, freeze and petrify states.

Pathfinding crosses the language boundary. A* runs in Rust compiled to WASM, with obstacle data written directly into linear memory so there is no serialization and no FFI copy. The README gives the figure as roughly 0.2 ms. Scripting is stranger still: the engine supports 218 DSL commands through a custom parser and async executor, and also embeds a full Lua 5.4 runtime through wasmoon, with both sharing the same GameAPI. That is a lot of surface area, and it is the honest cost of running original game scripts unchanged.

## Installing Miu2D and running the stack locally

The repository is a pnpm monorepo pinned to pnpm 10.16.1, and the root package.json lists the scripts you need. There are no published releases in the repository, so plan on cloning it rather than installing a package.

The database and object storage come up first. docker-compose.yml defines a postgres:16 service mapped to host port 5533 and a MinIO service with its API on 9100 and console on 9101. The root script wraps both:

```bash
pnpm db:up
```

That starts the db and minio services. The Postgres data lands in ./.data/postgres and MinIO data in ./.data/minio, so a wipe is a directory delete. Credentials default to postgres/postgres and minio/minio123, and .env.example lists POSTGRES_DB as miu2d_db and MINIO_BUCKET as miu2d. Copy that file to .env before starting anything that reads it.

Next, install dependencies and apply migrations, then run the development servers. The dev script runs the web app, the server and the types package together through Turborepo:

```bash
pnpm install
pnpm db:migrate
pnpm dev
```

The README does not state which port the web app binds to, but .env.example sets APP_URL to http://localhost:5274, which is the value the project ships for local use. The web build script is more explicit about ordering than the dev script: it builds @miu2d/types, generates the Prisma client, generates the tRPC bindings, builds the engine, then builds the web app. If you hit missing generated types, run pnpm build:web once before pnpm dev.

The Rust side is separate. The README does not document a Rust toolchain version, but the build is wrapped:

```bash
pnpm build:wasm
```

That calls scripts/build-wasm.sh. If the WASM artifact is stale or absent, expect pathfinding and the binary decoders to fail rather than degrade gracefully, because the engine treats them as required.

## Where Miu2D is the wrong choice

The engine is tuned for one genre. Combat, faction logic, NPC AI, 22 MoveKind trajectories and 10 SpecialKind effects, plus a passive XiuLian system, are all built around action RPG mechanics. If you are writing a puzzle game or a card game, you are paying for an 8-level character inheritance chain (Sprite, CharacterBase, Movement, Combat, Character, PlayerBase, PlayerCombat, Player) and a 218-command script VM you will never call.

The data model is the sharper constraint. Rebuilding three Kingsoft titles means the decoders, the SHD shadow format and the script commands exist to serve those games' binaries. Adopting Miu2D for a new project means either producing assets in those formats or writing your own decoders against the same interfaces. The README describes the decoders but does not present them as a documented, versioned asset specification you can target from an external toolchain.

Operationally, this is not a static bundle. The backend is Hono, tRPC 11 and Prisma over 21 PostgreSQL tables with 19 routers, plus MinIO or S3 for assets. A single-player game that never needs accounts, saved state on a server or uploaded assets inherits a database and an object store it does not use. The README also flags the project as developed with AI-assisted programming from day one, which is a statement about process, not about correctness, but it does mean the codebase's conventions were formed quickly and may not be uniform across all 215 engine files.

## Miu2D compared with Phaser and PixiJS

The README names PixiJS, Phaser and WASM-compiled Unity or Godot builds as the paths most web game projects take, and the difference is real. PixiJS is a renderer: it gives you a display list, sprites, filters and a WebGL or WebGPU backend, and leaves game logic, physics, input mapping and asset pipelines to you or to plugins. Phaser is a fuller framework with scenes, a physics system, input, tweens and an audio manager, and it assumes you will build your game in its structures.

Miu2D sits on the other side of that line. It owns the render loop and talks to WebGLRenderingContext directly, but it also owns the game semantics: factions, damage formulas, knockback, death and respawn, spell trajectories, a script VM and a Lua runtime. That is more than a framework and less than a general one, because the semantics are ARPG-specific.

The concrete trade-off is abstraction tax versus integration work. With Phaser you get a scene graph and a physics engine for free and spend your time bending them toward RPG behaviour. With Miu2D you get RPG behaviour already assembled, and you spend your time on asset formats and the backend. Neither is better in the abstract. The choice depends on whether your data already looks like a 2001 wuxia RPG.

## Licence, backend surface and the cost of keeping it running

Miu2D is MIT licensed, which permits commercial use, modification and redistribution provided the copyright notice and permission notice are preserved. That is a permissive licence, and it does not carry the copyleft obligations of the GPL family. It also means the project offers no warranty, and the README does not describe a support arrangement. The three rebuilt Kingsoft games are a separate question from the engine's licence: MIT covers Miu2D's own code, not the original games' assets or characters, and the repository does not state what rights the rebuilds rely on. If you intend to ship anything derived from those titles, that is a question for a lawyer, not for this page.

The upgrade cost is dominated by generated code and infrastructure, not by the engine. The build order in package.json runs types, then Prisma generation, then tRPC generation, then the engine, then the web app, so a schema change ripples through the client bindings. Zod 4 schemas are shared between client and server, which keeps validation in one place but means a schema edit is a cross-package edit. PostgreSQL 16 and MinIO are pinned in docker-compose.yml, and the Python tooling in pyproject.toml is separate again: it requires Python 3.12 or newer, defaults to the lightweight base group (Pillow, numpy, zstandard), and keeps the heavy AI and LoRA groups out of a default uv sync because they pull torch and CUDA 12.8 wheels. The project's own comment says to install those on demand. That is a sensible default, but it means the asset pipeline has two dependency systems to keep current.

## Conclusion

Adopt Miu2D if you are studying how a complete 2D ARPG is assembled, or if you are porting a tile-and-sprite RPG whose data formats you already control; the engine package is pure TypeScript with no React dependency, so it can be read on its own. Do not adopt it as a general 2D game engine for a platformer, a roguelike with procedural levels, or a 3D title: the subsystems are built around ARPG mechanics such as factions, MoveKind trajectories and NPC AI. Before committing, verify that you can build the WASM package with scripts/build-wasm.sh on your toolchain, that the PostgreSQL and MinIO services in docker-compose.yml come up on ports 5533, 9100 and 9101, and that the engine's binary decoders match the asset formats you actually hold.

## FAQ

### What engine do 2D games use?

There is no single answer, and Miu2D is one data point in the other direction: it uses no engine at all, rendering through raw WebGL with no dependency on Unity, Godot, Phaser, PixiJS or any other game framework. The README contrasts this with the more common route of PixiJS, Phaser or a WASM-compiled Unity or Godot build.

### What is a good game engine for 2D RPGs?

Miu2D is built specifically for 2D action RPGs: it ships combat, faction logic, NPC AI, 22 MoveKind trajectories, 10 SpecialKind effects and a 218-command script VM, and the README says it has been used to rebuild three classic Kingsoft wuxia RPGs. Whether it is good for your RPG depends on whether your assets resemble those games' binary formats.

### How do I install and run Miu2D locally?

Clone the repository and use the pnpm scripts: pnpm db:up starts PostgreSQL and MinIO from docker-compose.yml, then pnpm install, pnpm db:migrate and pnpm dev bring up the stack. The Rust pathfinder needs pnpm build:wasm, which calls scripts/build-wasm.sh.

### Does Miu2D need a database and object storage?

The backend package @miu2d/server runs Hono, tRPC and Prisma over 21 PostgreSQL tables, with MinIO or S3 for file storage, and docker-compose.yml defines both services. A purely client-side game would not use them, but the repository's default setup assumes they are running.

### What licence does Miu2D use?

The repository is MIT licensed, which allows commercial use and modification as long as the copyright and permission notices are kept. The licence covers the engine code; the repository does not state what rights apply to the original Kingsoft game assets.

### Can Miu2D run without WebGL?

The renderer module includes a Canvas2D fallback alongside the raw WebGL path, so the engine has a non-WebGL route. The README does not describe what visual features are lost on that path.

## Sources

- [Official documentation](https://miu2d.com)
- [Official README](https://github.com/luckyyyyy/miu2d#readme)
- [Project repository](https://github.com/luckyyyyy/miu2d)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/luckyyyyy-miu2d
