Universal Docker build environment for Emscripten/WASM/Deno/Meson C/C++ projects.
Includes the Emscripten ports system for convenience and upstream compatibility.
- Use for: Quick prototyping, upstream tutorials, auto-dependency downloads
- Ports available: SDL2, zlib, libpng, freetype, and 20+ more
- Auto-download: Use
-sUSE_ZLIB,--use-port=sdl2, etc.
Removes the Emscripten ports system to enforce explicit dependencies.
- Use for: Production builds with
@discere-os/*.wasmpackages - Benefits: No auto-downloads, explicit dependency control, smaller image
- Architecture: Aligns with SIDE_MODULE/MAIN_MODULE design
- Emscripten 4.0.16: Built from source (LLVM, Binaryen, Emscripten)
- WABT 1.0.38: WebAssembly Binary Toolkit (wat2wasm, wasm2wat, etc.)
- Complete control: Exact git revisions from upstream DEPS file
- Pre-built cache: Standard libraries compiled with embuilder
- Deno v2.5.4: Modern JavaScript/TypeScript runtime
- Node.js 22: Latest LTS with pnpm package manager
- Wrangler: Cloudflare Workers CLI for edge deployment
- Meson + Ninja: Fast, modern build system
- CMake: Cross-platform build system
- Build tools only: No system libraries (use
*.wasmforks for dependencies)
# Pull the standard image (with ports)
docker pull ghcr.io/discere-os/wasm-builder:latest
# Or pull the minimal image (no ports)
docker pull ghcr.io/discere-os/wasm-builder:minimal
# Build a WASM project with Deno
docker run --rm -v $(pwd):/workspace ghcr.io/discere-os/wasm-builder:latest \
deno task build:wasm
# Build with Meson
docker run --rm -v $(pwd):/workspace ghcr.io/discere-os/wasm-builder:latest \
bash -c "meson setup build && ninja -C build"
# Direct Emscripten compilation
docker run --rm -v $(pwd):/workspace ghcr.io/discere-os/wasm-builder:latest \
emcc main.c -o main.js -O3./scripts/test-quick.sh
# ~60-90 minutes, ~2-3GB image./scripts/test-local-build.shNote: Building LLVM from source takes significant time (~30-60 minutes) and resources. The multi-stage build optimizes the final image size by discarding build artifacts.
See scripts/README.md for DNS and other troubleshooting.
jobs:
build:
runs-on: ubuntu-latest
container:
image: ghcr.io/discere-os/wasm-builder:latest
steps:
- uses: actions/checkout@v4
- name: Build WASM
run: deno task build:wasm| Tool | Version | Source |
|---|---|---|
| Emscripten | 4.0.16 | Built from git 00c5c7d |
| LLVM/Clang | (DEPS) | Built from git 07ca4db |
| Binaryen | (DEPS) | Built from git c326e66 |
| WABT | 1.0.38 | Built from git tag |
| Deno | 2.5.4 | deno.land install script |
| Node.js | 22 | NodeSource repository |
| pnpm | latest | npm global install |
| Wrangler | latest | npm global install |
| Meson | β₯1.3.0 | pip3 install |
| Ninja | latest | Ubuntu apt |
| CMake | 3.x | Ubuntu apt |
Emscripten, LLVM, Binaryen: Exact git commits from the emscripten-releases DEPS file for version 4.0.16. These commits are guaranteed compatible by the Emscripten team.
WABT: Not required by Emscripten but included for development convenience. Provides tools like wat2wasm, wasm2wat, wasm-objdump, and wasm-validate.
Validation: All tools are verified during build with emcc --check to ensure correct configuration and compatibility.
Emscripten includes a ports system that auto-downloads and builds 25+ third-party libraries:
# Standard variant - ports enabled
emcc main.c -sUSE_ZLIB # Auto-downloads zlib 1.3.1 from GitHub
emcc main.c --use-port=sdl2 # Auto-downloads SDL2 from emscripten-portsProblems with ports for our use case:
- Downloads from external URLs (not reproducible)
- Fixed versions (can't customize or patch)
- Auto-triggered implicitly (hard to track)
- No SIDE_MODULE support (only static linking)
- Conflicts with our
@discere-os/*.wasmarchitecture
Solution: Two variants
| Scenario | Image | Why |
|---|---|---|
| Production builds | :minimal |
Explicit control, reproducible, our forks |
| Quick prototyping | :latest |
Convenience, upstream compatibility |
| Following upstream tutorials | :latest |
Tutorials assume ports are available |
| SIDE_MODULE architecture | :minimal |
Ports don't support SIDE_MODULE |
NO system libraries included (by design). Dependencies are handled via client/emscripten/*.wasm forks:
# No dependencies at build time
- run: deno task build:wasm
# Dependencies loaded at runtime via dlopen()# Clone and build dependencies from *.wasm forks
- name: Checkout zlib.wasm dependency
uses: actions/checkout@v4
with:
repository: discere-os/zlib.wasm
path: zlib.wasm
- name: Build zlib dependency
run: cd zlib.wasm && ./build-dual.sh side
# Creates zlib.wasm/install/lib/libz.a for static linking
- name: Build with dependency
run: deno task build:wasm
# Meson finds libz.a via PKG_CONFIG_PATHThis prevents system library pollution and ensures:
- Consistent WASM-native dependencies across all builds
- Version control via our own forks, not system packages
- Proper SIDE_MODULE architecture (runtime
dlopen()) - Static linking for MAIN_MODULE from our own builds
EMSCRIPTEN_VERSION=4.0.14EMSDK=/emsdkEM_CONFIG=/emsdk/.emscriptenEM_CACHE=/emsdk/.emscripten_cache
The image is optimized for:
- Fast builds: Emscripten cache is pre-warmed
- Small size: Minimal layers, cleaned apt cache
- Reproducibility: Pinned versions for all major tools
- Security: Latest Ubuntu 24.04 LTS base
This Docker build environment is part of a larger effort to bring professional desktop applications to browsers with native performance.
π¨βπ» About the Maintainer: Isaac Johnston (@superstructor) - Building foundational browser-native computing infrastructure through systematic C/C++ to WebAssembly porting.
π Impact: 70+ open source WASM libraries enabling professional applications like Blender, GIMP, and scientific computing tools to run natively in browsers.
π Your Support Enables:
- Continued maintenance and updates
- Performance optimizations
- New library ports and integrations
- Documentation and tutorials
- Cross-browser compatibility testing
π Sponsor this work to help build the future of browser-native computing.
MIT
Superstruct Ltd