Web (Emscripten)
This guide covers building ESEngine games for deployment in web browsers using Emscripten.
Build Configuration
CMake Options
emcmake cmake -B build_web \ -DES_BUILD_WEB=ON \ -DCMAKE_BUILD_TYPE=Release
cmake --build build_webOutput Files
After building, you’ll have:
build_web/├── MyGame.html # Standalone HTML page├── MyGame.js # Emscripten glue code├── MyGame.wasm # WebAssembly binary└── MyGame.data # Asset bundle (if using PRELOAD)Embedding in HTML
Standalone Page
The generated .html file works out of the box:
npx serve build_web# Open http://localhost:3000/MyGame.htmlCustom HTML
<!DOCTYPE html><html><head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>My Game</title> <style> body { margin: 0; background: #000; } #canvas { display: block; width: 100vw; height: 100vh; } </style></head><body> <canvas id="canvas"></canvas> <script> var Module = { canvas: document.getElementById('canvas'), onRuntimeInitialized: function() { console.log('Game loaded!'); } }; </script> <script src="MyGame.js"></script></body></html>Asset Loading
Preloading (Recommended)
Bundle assets into .data file at build time:
target_link_options(MyGame PRIVATE "SHELL:--preload-file ${CMAKE_SOURCE_DIR}/assets@/assets")Access normally in code:
Texture texture = Texture::load("/assets/player.png");Runtime Loading
Load assets via HTTP at runtime:
void onInit() override { // Async load Texture::loadAsync("https://example.com/player.png", [this](Texture tex) { playerTexture = tex; }, [](const std::string& error) { ES_LOG_ERROR("Failed to load: {}", error); } );}Module Configuration
Memory
target_link_options(MyGame PRIVATE "SHELL:-s INITIAL_MEMORY=64MB" "SHELL:-s ALLOW_MEMORY_GROWTH=1" "SHELL:-s MAXIMUM_MEMORY=256MB")WebGL
target_link_options(MyGame PRIVATE "SHELL:-s USE_WEBGL2=1" # WebGL 2.0 "SHELL:-s MIN_WEBGL_VERSION=2" "SHELL:-s MAX_WEBGL_VERSION=2")Optimizations
target_link_options(MyGame PRIVATE "SHELL:-O3" "SHELL:-flto" "SHELL:-s ASSERTIONS=0" "SHELL:--closure 1")target_link_options(MyGame PRIVATE "SHELL:-O0" "SHELL:-g" "SHELL:-s ASSERTIONS=2" "SHELL:-s SAFE_HEAP=1")Browser APIs
Local Storage
#include <emscripten.h>
void saveGame(const std::string& data) { EM_ASM({ localStorage.setItem('savedata', UTF8ToString($0)); }, data.c_str());}
std::string loadGame() { char* result = (char*)EM_ASM_PTR({ var data = localStorage.getItem('savedata') || ''; var len = lengthBytesUTF8(data) + 1; var buf = _malloc(len); stringToUTF8(data, buf, len); return buf; }); std::string data(result); free(result); return data;}Fullscreen
void toggleFullscreen() { EM_ASM({ if (!document.fullscreenElement) { Module.canvas.requestFullscreen(); } else { document.exitFullscreen(); } });}Deployment
GitHub Pages
-
Build with correct base path
Terminal window emcmake cmake -B build_web -DES_BUILD_WEB=ONcmake --build build_web -
Copy to
docs/orgh-pagesbranchTerminal window cp build_web/MyGame.* docs/ -
Enable GitHub Pages in repository settings
itch.io
- Zip the build output files
- Upload to itch.io as HTML5 game
- Set viewport size in itch.io dashboard
Cloudflare Pages / Vercel
Just point to your build output directory. Both platforms serve static files directly.
Performance Tips
Troubleshooting
| Issue | Solution |
|---|---|
| Blank screen | Check browser console for errors |
| Memory error | Increase INITIAL_MEMORY |
| Slow loading | Enable compression on server |
| Touch not working | Ensure canvas has tabindex="0" |