Web / Emscripten
micro supports building for the browser via Emscripten. The game loop, window, and renderer work transparently — no changes to your game code are required.
Prerequisites
- Emscripten SDK installed and
activated:
bash emsdk install latest emsdk activate latest source ./emsdk_env.sh # Linux / macOS emsdk_env.bat # Windows - CMake 3.15+
Building
Use emcmake to wrap the CMake configure step so that the Emscripten toolchain is applied
automatically:
emcmake cmake -S . -B build-web -DMICRO_BUILD_EXAMPLES=ON
cmake --build build-web
Each example is compiled to a self-contained .html file alongside a .js and .wasm file in
the build output directory.
Running in the browser
Browsers block local file:// access for security reasons, so the output must be served from a
static file server:
# Python — usually the quickest option
python -m http.server 8080 --directory build-web/examples/minimal
Then open http://localhost:8080/minimal.html.
How the loop works
window::run() uses a plain blocking while loop on all platforms. On Emscripten this works
transparently via Asyncify (-sASYNCIFY=1), a compile-time transformation that rewrites
the Wasm binary so the loop can yield to the browser between frames without any changes to your
code. The resulting binary uses only standard WebAssembly and is supported by all modern browsers.
// Identical code on native and in the browser — no #ifdefs, no callbacks
win.run([&](float dt) {
pos += vel * dt;
rend.clear(micro::color::black);
// draw things…
rend.present();
});
Fullscreen
Calling window::set_fullscreen(true) delegates to the browser Fullscreen API. The browser
requires this call to originate from a user gesture (a key press or a mouse click); requests
made outside one are silently ignored.
win.run([&](float dt) {
if (kb.just_pressed(micro::key::f)) {
win.set_fullscreen(!win.fullscreen());
}
// …
});
Asset loading
std::filesystem paths are resolved inside the Emscripten virtual file system (a simple
in-memory FS that exists only for the duration of the page). To bundle assets with the build,
add a --preload-file link option to your target:
if(EMSCRIPTEN)
target_link_options(mygame PRIVATE
"SHELL:--preload-file ${CMAKE_SOURCE_DIR}/assets@/assets"
)
endif()
The src@dst syntax mounts your local assets/ directory at /assets inside the VFS.
Use the absolute VFS path in your C++ code to be explicit:
auto tex = micro::texture::load(rend, "/assets/sprite.png");
auto snd = micro::audio::sound::load(mix, "/assets/theme.ogg");
!!! note
Emscripten's default working directory is /, so the relative path
"assets/sprite.png" also resolves correctly — but the leading / makes
the intent unambiguous.
Complete example
A minimal project with web support enabled:
CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(mygame)
add_subdirectory(micro)
add_executable(mygame main.cpp)
target_link_libraries(mygame PRIVATE micro)
if(EMSCRIPTEN)
set_target_properties(mygame PROPERTIES SUFFIX ".html")
target_link_options(mygame PRIVATE
-sASYNCIFY=1
-sALLOW_MEMORY_GROWTH=1
"SHELL:--preload-file ${CMAKE_SOURCE_DIR}/assets@/assets"
)
endif()
Configure and build:
emcmake cmake -S . -B build-web
cmake --build build-web
Serve and open:
python -m http.server 8080 --directory build-web
# → http://localhost:8080/mygame.html
The build directory will contain mygame.html, mygame.js, and mygame.wasm (plus
mygame.data if --preload-file was used). Opening the .html file is all that is needed.
Limitations
| Feature | Status |
|---|---|
| Window & renderer | ✅ Fully supported |
| Keyboard & mouse input | ✅ Fully supported |
| Logical resolution / fullscreen | ✅ Supported (fullscreen requires user gesture) |
| Image & texture loading | ✅ Supported (use --preload-file for assets) |
| Font rendering | ✅ Supported |
| Audio | ✅ Supported (use --preload-file for audio assets) |
| Threading | ℹ️ Single-threaded by default |
Further reading
- Emscripten documentation — the official reference, covering the toolchain, porting guide, and all available settings
- Asyncify — details on how the blocking
loop transformation works and available tuning options (
ASYNCIFY_ONLY, etc.) - Emscripten file system API —
full reference for the virtual FS,
--preload-file,--embed-file, and programmatic access - SDL3 Emscripten notes — SDL3-specific considerations when targeting the browser
- WebAssembly browser compatibility — current Wasm support across browsers