Covers build issues, runtime debugging, browser dev tools, deployment sizing, embedding, and known limitations for Emscripten/WebAssembly builds. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
6.8 KiB
WASM / Emscripten Troubleshooting Guide
Practical solutions for common issues when building, testing, and deploying McRogueFace as a WebAssembly application.
Build Issues
"emcmake not found"
The Emscripten SDK must be activated in your current shell before building:
source ~/emsdk/emsdk_env.sh
make wasm
This sets PATH, EMSDK, and other environment variables. You need to re-run it for each new terminal session.
Build fails during CMake configure
If CMake fails during the Emscripten configure step, delete the build directory and re-configure:
rm -rf build-emscripten
make wasm
The Makefile targets skip CMake if a Makefile already exists in the build directory. Stale CMake caches from a prior SDK version or changed options cause configure errors.
"memory access out of bounds" at startup
Usually caused by insufficient stack or memory. The build defaults to a 2 MB stack (-sSTACK_SIZE=2097152) and growable heap (-sALLOW_MEMORY_GROWTH=1). If you hit stack limits with deep recursion (e.g. during Python import), increase the stack size in CMakeLists.txt:
-sSTACK_SIZE=4194304 # 4 MB
Link errors about undefined symbols
The Emscripten build uses -sERROR_ON_UNDEFINED_SYMBOLS=0 because some libc/POSIX symbols are stubbed. If you add new C++ code that calls missing POSIX APIs, you will get a runtime error rather than a link error. Check the browser console for Aborted(Assertion failed: missing function: ...).
Runtime Issues
Python import errors
The WASM build bundles a filtered Python stdlib at build time via --preload-file. If a Python module is missing at runtime:
- Check
wasm_stdlib/lib/— this is the preloaded stdlib tree - If the module should be included, add it to
tools/stdlib_modules.yamlunder the appropriate category - Rebuild:
rm -rf build-emscripten && make wasm
Some modules (like socket, ssl, multiprocessing) are intentionally excluded because they require OS features unavailable in the browser.
"Synchronous XMLHttpRequest on the main thread is deprecated"
This warning appears when Python code triggers synchronous file I/O during module import. It's harmless but can cause slight UI freezes. The engine preloads all files into Emscripten's virtual filesystem before Python starts, so actual network requests don't happen.
IndexedDB / persistent storage errors
The build uses -lidbfs.js for persistent storage (save games, user preferences). Common issues:
- "mkdir failed" on first load: The engine calls
FS.mkdir('/idbfs')during initialization. If the path already exists from a prior version, this fails silently. Theemscripten_pre.jsfile patches this. - Data not persisting: Call
FS.syncfs(false, callback)from JavaScript to flush changes to IndexedDB. The C++ side exposessync_storage()viaModule.ccall. - Private browsing: IndexedDB is unavailable in some private/incognito modes. The engine falls back gracefully but data won't persist.
Black screen / no rendering
Check the browser's developer console (F12) for errors. Common causes:
- WebGL 2 not supported: The build requires WebGL 2 (
-sMIN_WEBGL_VERSION=2). Very old browsers or software renderers may not support it. - Canvas size is zero: If the HTML container has no explicit size, the canvas may render at 0x0. The custom
shell.htmlhandles this, but custom embedding needs to set canvas dimensions. - Exception during init: A Python error during
game.pyexecution will abort rendering. Check console for Python tracebacks.
Audio not working
Audio is stubbed in the WASM build. SoundBuffer, Sound, and Music objects exist but do nothing. This is documented in the Web Build Constraints table in CLAUDE.md.
Debugging
Enable debug builds
Use the debug WASM targets for full DWARF symbols and source maps:
make wasm-debug # Full game with debug info
make playground-debug # REPL with debug info
These produce larger binaries but enable:
- Source-level debugging in Chrome DevTools (via DWARF and source maps)
- Readable stack traces (via
--emit-symbol-map)
Reading WASM stack traces
Production WASM stack traces show mangled names like $_ZN7UIFrame6renderEv. To demangle:
- Use the debug build which emits a
.symbolsfile - Or pipe through
c++filt:echo '_ZN7UIFrame6renderEv' | c++filt - Or use Chrome's DWARF extension for inline source mapping
Browser developer tools
- Chrome: DevTools > Sources > shows C++ source files with DWARF debug builds
- Firefox: Debugger > limited DWARF support, better with source maps
- Console: All
printf/std::coutoutput goes to the browser console - Network: Check that
.data(preloaded files) and.wasmloaded successfully - Memory: Use Chrome's Memory tab to profile WASM heap usage
Assertions
The build enables -sASSERTIONS=2 and -sSTACK_OVERFLOW_CHECK=2 by default (both debug and release). These catch:
- Null pointer dereferences in WASM memory
- Stack overflow before it corrupts the heap
- Invalid Emscripten API usage
Deployment
File sizes
Typical build sizes:
| Build | .wasm | .data | .js | Total |
|---|---|---|---|---|
| Release | ~15 MB | ~25 MB | ~200 KB | ~40 MB |
| Debug | ~40 MB | ~25 MB | ~300 KB | ~65 MB |
The .data file contains the Python stdlib and game assets. Use the "light" stdlib preset to reduce it.
Serving requirements
WASM files require specific HTTP headers:
Content-Type: application/wasmfor.wasmfiles- CORS headers if serving from a CDN
The make serve targets use Python's http.server which handles MIME types correctly for local development.
Embedding in custom pages
The build produces an HTML file from shell.html (or shell_game.html). To embed in your own page, you need:
- The
.js,.wasm, and.datafiles from the build directory - A canvas element with
id="canvas" - Load the
.jsfile, which bootstraps everything:
<canvas id="canvas" width="1024" height="768"></canvas>
<script src="mcrogueface.js"></script>
Game shell vs playground shell
make wasm/make wasm-game: Usesshell.htmlorshell_game.html— includes REPL widget or fullscreen canvasmake playground: Usesshell.htmlwith REPL chrome — intended for interactive testing- Set
MCRF_GAME_SHELL=ONin CMake for fullscreen-only (no REPL)
Known Limitations
- No dynamic module loading: All Python modules must be preloaded at build time
- No threading: JavaScript is single-threaded; Python's
threadingmodule is non-functional - No filesystem writes to disk: Writes go to an in-memory filesystem (optionally synced to IndexedDB)
- No audio: Sound API is fully stubbed
- No ImGui console: The debug overlay is desktop-only
- Input differences: Some keyboard shortcuts are intercepted by the browser (Ctrl+W, F5, etc.)