by DonHopkins | 2025-07-23 15:35:22+00:00
Emscripten has a handy tool called "Embind" for binding JavaScript/TypeScript and C/C++/whatever code. It's underappreciated and not well documented all in one place, but here is a soup-to-nuts summary.

Emscripten + Embind allow you to subclass and implement C++ interfaces in TypeScript, and easily call back and forth, even pass typed function pointers back and forth, using them to call C++ from TypeScript and TypeScript from C++!

Embind: https://emscripten.org/docs/porting/connecting_cpp_and_javas...

Interacting with Code: https://emscripten.org/docs/porting/connecting_cpp_and_javas...

Embind's bind.cpp plumbing: https://github.com/emscripten-core/emscripten/blob/main/syst...

C Emscripten macros (like EM_ASM_): https://livebook.manning.com/book/webassembly-in-action/c-em...

Emscripten’s embind: https://web.dev/articles/embind

I'm using it for the WASM version of Micropolis (open source SimCity). The idea is to be able to cleanly separate the C++ simulator from the JS/HTML/WebGL user interface, and also make plugin zones and robots (like the monster or tornado or train) by subclassing C++ interface and classes in type safe TypeScript!

emscripten.cpp binds the C++ classes and interfaces and structs to JavaScript using the magic plumbing in "#include <emscripten/bind.h>".

There is an art to coming up with an elegant interface at the right level of granularity that passes parameters efficiently (using zero-copy shared memory when possible, i.e. C++ SimCity Tiles <=> JS WebGL Buffers for the shader that draws the tiles) -- see the comments in the file about that):

emscripten.cpp: https://github.com/SimHacker/MicropolisCore/blob/main/Microp...

/** * @file emscripten.cpp * @brief Emscripten bindings for Micropolis game engine. * * This file contains Emscripten bindings that allow the Micropolis * (open-source version of SimCity) game engine to be used in a web * environment. It utilizes Emscripten's Embind feature to expose C++ * classes, functions, enums, and data structures to JavaScript, * enabling the Micropolis game engine to be controlled and interacted * with through a web interface. This includes key functionalities * such as simulation control, game state management, map * manipulation, and event handling. The binding includes only * essential elements for gameplay, omitting low-level rendering and * platform-specific 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. [...] class_<Callback>("Callback") .function("autoGoto", &Callback::autoGoto, allow_raw_pointers()) [...]

Here's the WebGL tile renderer that draws the tiles directly out of a Uint16Array pointing into WASM memory:

https://github.com/SimHacker/MicropolisCore/blob/main/microp...

The corresponding C++ source and header and TypeScript files define the callback interface and plumbing:

callback.h defines the abstract Callback interface, as well as a ConsoleCallback interface that just logs to the JS console, for debugging:

callback.h: https://github.com/SimHacker/MicropolisCore/blob/main/Microp...

/** * @file callback.h * @brief Interface for callbacks in the Micropolis game engine. * * This file defines the Callback class, which serves as an interface * for various callbacks used in the Micropolis game engine. These * callbacks cover a wide range of functionalities including UI * updates, game state changes, sound effects, simulation events, and * more. The methods in this class are virtual and intended to be * implemented by the game's frontend to interact with the user * interface and handle game events. */ class Callback { public: virtual ~Callback() {} virtual void autoGoto(Micropolis *micropolis, emscripten::val callbackVal, int x, int y, std::string message) = 0; [...]

callback.cpp implements just the concrete ConsoleCallback interface in C++ with "EM_ASM_" glue to call out to JavaScript to simply log the parameters of each call:

callback.cpp: https://github.com/SimHacker/MicropolisCore/blob/main/Microp...

/** * @file callback.cpp * @brief Implementation of the Callback interface for Micropolis game * engine. * * This file provides the implementation of the Callback class defined * in callback.h. It includes a series of methods that are called by * the Micropolis game engine to interact with the user interface. * These methods include functionalities like logging actions, * updating game states, and responding to user actions. The use of * EM_ASM macros indicates direct interaction with JavaScript, typical * in a web environment using Emscripten. */

js_callback.h contains an implementation of the Callback interface that caches a "emscripten::val jsCallback" (an enscripten value reference to a JS object that implements the interface), and uses jsCallback.call to make calls to JavaScript:

js_callback.h: https://github.com/SimHacker/MicropolisCore/blob/main/Microp...

#include <emscripten/val.h> #include "callback.h" class JSCallback : public Callback { public: explicit JSCallback(emscripten::val jsCallback) : Callback(), jsCallback(jsCallback) {} // Implement all pure virtual functions from Callback void autoGoto(Micropolis *micropolis, emscripten::val callbackVal, int x, int y, std::string message) override { jsCallback.call<void>("autoGoto", emscripten::val(micropolis), callbackVal, x, y, message); } [...] private: emscripten::val jsCallback; };

Then emscripten/embind generates a TypeScript file that defines the JS side of things:

micropolisengine.d.ts: https://github.com/SimHacker/MicropolisCore/blob/main/microp...

// TypeScript bindings for emscripten-generated code. Automatically generated at compile time. [...] export interface Callback { autoGoto(_0: Micropolis, _1: any, _2: number, _3: number, _4: EmbindString): void; [...] export type MainModule = WasmModule & typeof RuntimeExports & EmbindModule; export default function MainModuleFactory (options?: unknown): Promise<MainModule>;

Then you can import that TypeScript interface (using a weird "<reference path=" thing I don't quite understand but is necessary), and implement it in nice clean safe TypeScript:

https://github.com/SimHacker/MicropolisCore/blob/main/microp...

/// <reference path="../types/micropolisengine.d.ts" /> import type { Micropolis, JSCallback } from '../types/micropolisengine.d.js'; // Micropolis Callback Interface Implementation export class MicropolisCallbackLog implements JSCallback { verbose: boolean = false; autoGoto(micropolis: Micropolis, callbackVal: any, x: number, y: number, message: string): void { console.log('MicropolisCallbackLog: autoGoto:', 'x:', x, 'y:', y, 'message:', message); }

It's all nice and type safe, and Doxygen will even generate documentation for you:

https://micropolisweb.com/doc/classJSCallback.html

And it even works, and it's pretty fast! (Type "9" to go super fast, but for the love of god DO NOT PRESS THE SPACE BAR!!!)

https://micropolisweb.com