Here’s a tutorial about using Emscripten to run D code in a normal web browser. It’s uses a different approach from
the Dscripten game demo and the dscripten-tools toolchain that’s based on it.
Instead of porting the D runtime, it uses a lightweight, runtimeless -betterC build.
It uses Docker to manage the Emscripten installation.
LDC has recently gained support for compiling
directly to WebAssembly, but (unlike the Emscripten approach) that doesn’t automatically get you libraries.
You can find the complete working code on Github.
./run.sh starts a shell in a Docker image that contains the
development environment. dub build --build=release generates
the HTML and JavaScript assets and puts them into the dist/
directory.
Emscripten is a compiler toolchain for asm.js and
WebAssembly that comes with ported versions of the libc and SDL2 C libraries. It can compile regular Linux-based
applications in languages like C to code that can run in a browser.
How do you use Emscripten with D?
Emscripten is a toolchain designed for C/C++, but the C/C++ part is just a frontend. The toolchain actually compiles
LLVM intermediate representation (IR). You can generate LLVM IR bitcode from D using LDC, so it should be possible to feed that through Emscripten and run D in a browser,
just like C/C++.
Gotchas using Emscripten
Ideally that’s all it would take, but there are some things that require special attention (or trial and error).
D runtime library features like GC and Phobos can’t be used without an Emscripten port.
It’s not enough to just produce LLVM IR. The code needs to meet Emscripten’s requirements.
It needs to use ported libraries.
Pointer sizes and data structure binary layouts need to match.
Emscripten bugs need to be worked around.
Debug information is particularly problematic.
Implementation
Plan of attack
Here’s the plan for making D+Emscripten development work:
Use -betterC and the @nogc and nothrow attributes to avoid D runtime features.
Use SDL2 functions directly by statically compiling with bindbc-sdl.
Keep on trying.
Environment setup
Emscripten is based on LLVM, clang and various other libraries, and is hard to set up, so I decided to do the job with Docker. I wrote a Dockerfile that would also add LDC
and other tools at docker build time:
Docker makes these big toolchains pretty easy :)
Coding
Here’s a basic demo that displays an image:
Building
Now building is the tricky bit.
dub.json
Here’s the dub.json I made through trial and error. It
runs the whole build from D to WebAssembly.
Switch to 32b (x86) code generation
Compiling with 64b “worked” but I got a warning about different data layouts:
Apparently Emscripten is basically for 32b code. Using mismatched pointer sizes sounds like a pretty bad idea, so I
added this /var/lib/dub/settings.json to the Dockerfile:
After lots of trial and error, I finally succeeded in getting my demo to run in a browser. Here’s how it looks:
The Emscripten+D dev environment isn’t as stable as a normal dev environment. For example, rendering didn’t work if
I used SDL_LowerBlit instead. But here’s D-Man in a
browser.