Building Browser Games with Raylib and WebAssembly
#raylib
#wasm
#browser-games
#tutorial
Introduction
Raylib is a small, simple, and easy-to-use C library for game development. WebAssembly (Wasm) lets you run native-like code in the browser with near-native performance. Combining Raylib with Wasm gives you a path to run cross-platform browser games without rewriting your engine or mechanics in JavaScript. This post walks through a practical workflow to compile Raylib code to Wasm and run it in a browser.
Why Raylib + WebAssembly for browser games
- Performance: WebAssembly runs near native speeds, suitable for 2D games and lightweight 3D projects.
- Reuse: Keep your existing Raylib C/C++ codebase and tools.
- Accessibility: Users can play directly in the browser without downloads or installers.
- Debuggability: Browser devtools help you inspect performance, memory, and rendering.
Prerequisites
- A C/C++ toolchain and build tools (gcc/clang, cmake, make)
- Emscripten SDK (emsdk) to compile C/C++ to WebAssembly
- Raylib source or prebuilt Raylib libraries for Emscripten
- Basic familiarity with C/C++ and the command line
Step 1: Set up the toolchain and Raylib for Wasm
- Install the Emsdk (Emscripten) and activate it:
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh
- Obtain Raylib (build for Wasm) and build it for Emscripten:
git clone https://github.com/raysan5/raylib.git
cd raylib
mkdir build_wasm
cd build_wasm
emcmake cmake ..
emmake make
- Note: If you prefer, you can also use a prebuilt Raylib Wasm distribution or follow the Raylib Wasm port’s instructions. The key idea is to have a Raylib library compiled for Emscripten available to link against.
Step 2: Write a simple Raylib program
Create a file named hello_raylib.c with a minimal Raylib program:
#include "raylib.h"
int main(void)
{
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "Raylib WASM Example");
SetTargetFPS(60);
while (!WindowShouldClose())
{
BeginDrawing();
ClearBackground(RAYWHITE);
DrawText("Hello, WebAssembly + Raylib!", 190, 200, 20, LIGHTGRAY);
EndDrawing();
}
CloseWindow();
return 0;
}
Step 3: Build the program for the web
Link against the Raylib library and export a few runtime methods if needed:
emcc hello_raylib.c \
-I /path/to/raylib/include \
-L /path/to/raylib/build_wasm \
-L /path/to/raylib/lib \
-lraylib \
-s USE_GLFW=3 \
-s WASM=1 \
-s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]' \
-O2 \
-o dist/index.html
- Tip: Replace /path/to/raylib with the actual paths from your setup. The exact EMCC flags can vary by Raylib version; consult the Raylib + Emscripten docs for the recommended flags for your build.
Step 4: Serve and test locally
- Serve the generated dist folder with a simple HTTP server:
cd dist
python3 -m http.server 8080
- Open http://localhost:8080 in your browser. You should see the window created by the Raylib program and the text “Hello, WebAssembly + Raylib!”.
Step 5: HTML wrapper and assets
When you build with emcc, it often generates an index.html, a main.js and a main.wasm. If you want a custom wrapper, you can provide your own HTML file that mounts the canvas Raylib uses and loads the generated script. A minimal wrapper might look like this:
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Raylib WASM</title>
</head>
<body>
<canvas id="canvas" width="800" height="450"></canvas>
<script src="main.js"></script>
</body>
</html>
- Make sure the canvas size matches the dimensions used by your program, or adjust in code accordingly.
Step 6: Deployment considerations
- Static hosting: GitHub Pages, Netlify, Vercel, or any static hosting works well for Wasm apps.
- Performance tweaks: Build with optimization (-O2 or -O3), enable memory growth if your app allocates dynamic memory, and consider disabling features you don’t need (e.g., audio) to reduce bundle size.
- Accessibility: Provide a fallback or informative message if Wasm is unavailable in the browser.
Tips and caveats
- Web APIs: Raylib’s Wasm build relies on browser-provided WebGL and input APIs. Expect small differences in input handling or windowing compared to native Raylib.
- Memory management: WebAssembly has a finite initial heap. Use memory growth options if your game requires dynamic allocations.
- Debugging: Use browser devtools for memory and performance profiling. Emscripten also provides runtime instrumentation options if needed.
- Alternatives: If you want a more JS-friendly path, explore raylib-wasm wrappers or ported bindings, or consider other engines designed for the web.
Next steps
- Experiment with more complex Raylib features (sprites, audio, events) in Wasm.
- Create a small game project template that you can reuse for browser games.
- Explore packaging multiple Raylib scenes or levels and loading assets asynchronously.
Further resources
- Raylib official: https://www.raylib.com/
- Emscripten: https://emscripten.org/
- Raylib wasm guides and examples: (reference the Raylib GitHub and documentation for up-to-date build flags and recommendations)
This workflow provides a practical path to bring Raylib-powered games to the browser with WebAssembly. As you iterate, you’ll refine the build flags, optimize assets, and tailor the HTML wrapper to your game’s needs.