I've been porting Micropolis (SimCity Classic) to WASM / WebGPU / Svelte 5. Emscripten + Embind compile the C++ engine and glue it to TypeScript/Svelte/Runes/Reactivity; TypeScript owns UI, rendering, and callback handlers.<p>I agree with the article's main lessons: wasm32 pointer size, don't serialize structs with pointers, debug native 32-bit when you can, WebGL/WebGPU is stricter than desktop GL, Emscripten export flags still bite. I hit some of the same categories; the parts that were actually tricky for Micropolis are below.<p>Svelte 5 runes ($state, $derived, etc.) work in plain .ts modules, not just .svelte templates. That matters because the WASM bridge is a reactive module the HUD, command bus, and Vitest all import -- not a component-only trick. The file has to be MicropolisReactive.svelte.ts so runes compile under the same Vite/SvelteKit pipeline as the app; plain .ts breaks in Node with "$state is not defined".<p>Embind API surface -- what to expose and what to leave out:<p><a href="https://github.com/SimHacker/MicropolisCore/blob/main/packages/micropolis-engine/src/emscripten.cpp" rel="nofollow">https://github.com/SimHacker/MicropolisCore/blob/main/packag...</a><p><pre><code> // This file uses emscripten's embind to bind C++ classes,
// C structures, functions, enums, and contents into JavaScript,
// so you can even subclass C++ classes in JavaScript,
// for implementing plugins and user interfaces.
//
// Wrapping the entire Micropolis class from the Micropolis (open-source
// version of SimCity) code into Emscripten for JavaScript access is a
// large and complex task, mainly due to the size and complexity of the
// class. The class encompasses almost every aspect of the simulation,
// including map generation, simulation logic, user interface
// interactions, and more.
</code></pre>
The comments in that file go on to describe the strategy for wrapping: Core Simulation Logic, Memory and Performance Considerations, Direct Memory Access, User Interface and Rendering, Callbacks and Interactivity, and Optimizations.<p>The engine callback virtual interface bridged C++ to JS via JSCallback:<p><a href="https://github.com/SimHacker/MicropolisCore/blob/main/packages/micropolis-engine/src/js_callback.h" rel="nofollow">https://github.com/SimHacker/MicropolisCore/blob/main/packag...</a><p>In the old NeWS/Hyperlook, TCL/Tk/X11, SWIG/Python/PyGTK, and SWIG/Python/TurboGears/AMF/Flash versions, this callback interface used to be a stringly typed general purpose event callback interface, which I tightened up into a strict C++ interface and corresponding typescript interface, so embind could help me integrate it safely and cleanly with TypeScript and Svelte Runes.<p>TypeScript handlers that update rune-backed state (sendMessage, didTool, budget hooks, etc.):<p><a href="https://github.com/SimHacker/MicropolisCore/blob/main/apps/micropolis/src/lib/MicropolisReactive.svelte.ts" rel="nofollow">https://github.com/SimHacker/MicropolisCore/blob/main/apps/m...</a><p>Simulator attach/detach, singleton engine load, wiring JSCallback into Micropolis:<p><a href="https://github.com/SimHacker/MicropolisCore/blob/main/apps/micropolis/src/lib/MicropolisSimulator.ts" rel="nofollow">https://github.com/SimHacker/MicropolisCore/blob/main/apps/m...</a><p>The pattern: C++ fires callbacks with enough context for the UI; TS updates $state; components read micropolisReactive (peek / poke / memory / getSnapshot) instead of calling Embind or touching HEAP* directly. That is where the rubber hits the road for interactivity.<p>Heap access is its own footgun. Emscripten may expose Module.wasmMemory, HEAPU16, or neither until init; some getters throw if you read too early. Centralized helper:<p><a href="https://github.com/SimHacker/MicropolisCore/blob/main/apps/micropolis/src/lib/wasm/heap.ts" rel="nofollow">https://github.com/SimHacker/MicropolisCore/blob/main/apps/m...</a><p>Bridge design, Vitest against real WASM, teardown order with Embind lifetimes:<p><a href="https://github.com/SimHacker/MicropolisCore/blob/main/documentation/designs/wasm-bridge-and-testing-trajectory.md" rel="nofollow">https://github.com/SimHacker/MicropolisCore/blob/main/docume...</a><p>Map rendering: WebGPU tile renderer with canvas fallback (legacy WebGL frozen, now reimplementing in WebGPU). The renderer reads 16 bit flags + tile indices from direct simulator memory views into WASM linear memory (mapData / mopData), not per-frame Embind copies.<p><a href="https://github.com/SimHacker/MicropolisCore/blob/main/packages/tile-renderer/src/WebGPUTileRenderer.ts" rel="nofollow">https://github.com/SimHacker/MicropolisCore/blob/main/packag...</a><p><a href="https://github.com/SimHacker/MicropolisCore/blob/main/documentation/designs/unified-webgpu-renderer.md" rel="nofollow">https://github.com/SimHacker/MicropolisCore/blob/main/docume...</a><p>City saves are a defined binary format (.cty), not fwrite of engine structs. Live map data is views into WASM linear memory (mapData / mopData), not embedded native pointers -- same idea as the article's side-table fix, but that is how this codebase is already structured.<p>Why I find this stack interesting: original SimCity engine lineage, narrow Embind surface on purpose, reactive TS facade so automation and UI share one sim without reviving the old Python/SWIG/pyGTK path. Sprites (trains, choppers, generic orange monsters wrecking chaos and havoc -- definitely not Godzilla [TM], but possibly Trump adjacent) simulate in C++; compositing them in the WebGPU path is still work in progress.<p>The WebGPU renderer is being built as a general stack with pluggable layers, including Sims content rendering (characters, animations, terrain, objects, walls, floors, ui effects, etc).<p>Character animation demo:<p><a href="https://vitamoo.space" rel="nofollow">https://vitamoo.space</a><p>VitaMoo code:<p><a href="https://github.com/SimHacker/MicropolisCore/tree/main/packages/vitamoo" rel="nofollow">https://github.com/SimHacker/MicropolisCore/tree/main/packag...</a><p>Unified WebGPU Renderer:<p><a href="https://github.com/SimHacker/MicropolisCore/blob/main/documentation/designs/unified-webgpu-renderer.md" rel="nofollow">https://github.com/SimHacker/MicropolisCore/blob/main/docume...</a><p>Render Core Package:<p><a href="https://github.com/SimHacker/MicropolisCore/blob/main/documentation/designs/render-core-package.md" rel="nofollow">https://github.com/SimHacker/MicropolisCore/blob/main/docume...</a><p>Renderer Plugin Roadmap:<p><a href="https://github.com/SimHacker/MicropolisCore/blob/main/documentation/designs/renderer-plugin-roadmap.md" rel="nofollow">https://github.com/SimHacker/MicropolisCore/blob/main/docume...</a><p>Live Micropolis tile renderer and simulator demo (no other ui yet, work in progress):<p><a href="https://micropolisweb.com" rel="nofollow">https://micropolisweb.com</a><p>Demo of the simulator, cellular automata, and tile engine to Jerry Martin's music:<p><a href="https://www.youtube.com/watch?v=319i7slXcbI" rel="nofollow">https://www.youtube.com/watch?v=319i7slXcbI</a><p>Repo:<p><a href="https://github.com/SimHacker/MicropolisCore" rel="nofollow">https://github.com/SimHacker/MicropolisCore</a>