Building Browser Games with Raylib and WebAssembly

Team 4 min read

#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

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.