WavedashDocs

raylib

Compile raylib games to WebAssembly with Emscripten and call the Wavedash SDK from C.

View example project on GitHub

raylib compiles to WebAssembly through its official Emscripten backend (PLATFORM=Web). You get the same BeginDrawing / EndDrawing / IsKeyDown API in the browser that you use on desktop — just link against a Web-platform build of libraylib.a.

Setup

You need:

  • The Emscripten SDK (activate with source ./emsdk_env.sh)
  • A Web-platform build of raylib. The example's build.sh clones raylib 5.5 and builds libraylib.a with emcmake cmake . -DPLATFORM=Web && emmake make raylib.

Point the build at your raylib source with RAYLIB_PATH=/path/to/raylib if you already have one.

SDK integration

Wavedash injects Wavedash into the page before your Emscripten module loads. Use EM_JS to call the SDK from C — same pattern as any Emscripten project:

Calling Wavedash.init() is required. Your game stays hidden behind the Wavedash loading screen until you do. Call it once your game is ready to play.

#include <raylib.h>
#include <emscripten.h>

EM_JS(void, wavedash_init, (), { Wavedash.init(); });
EM_JS(void, wavedash_progress, (double p), { Wavedash.updateLoadProgressZeroToOne(p); });

static void tick(void) {
    BeginDrawing();
    // ... draw frame ...
    EndDrawing();
}

int main(void) {
    InitWindow(900, 600, "My Game");
    SetTargetFPS(60);

    wavedash_progress(1.0);
    wavedash_init();

    emscripten_set_main_loop(tick, 0, 1);
    return 0;
}

Call wavedash_progress(...) with intermediate values during async asset loading. wavedash_init() automatically signals load completion, so call it last.

Build script

#!/usr/bin/env sh
set -eu
RAYLIB_DIR="${RAYLIB_PATH:-$PWD/raylib}"
mkdir -p build

emcc src/main.c -O2 \
  -I"$RAYLIB_DIR/src" \
  "$RAYLIB_DIR/src/raylib/libraylib.a" \
  -DPLATFORM_WEB \
  -s USE_GLFW=3 \
  -s ASYNCIFY \
  -s ALLOW_MEMORY_GROWTH=1 \
  -s MODULARIZE=0 \
  -s ENVIRONMENT=web \
  -s GL_ENABLE_GET_PROC_ADDRESS=1 \
  -o build/game.js

cp web/index.html build/index.html

USE_GLFW=3 and ASYNCIFY are the core flags raylib needs on Emscripten. Don't add -s FULL_ES3=1 — raylib's CMake Web build defaults to OpenGL ES 2, and mixing ES3 client-array emulation with an ES2 library leaves the GL context uninitialized at draw time. The --shell-file approach also works if you prefer raylib's generated HTML over a custom shell.

HTML shell

raylib's Emscripten port reads Module.canvas during GL context creation, so Module has to be set before game.js starts executing. And window.Wavedash is injected as a Promise — resolve it before the EM_JS bridge tries to call .init() synchronously. The cleanest way to satisfy both is to do both in a module script and then inject game.js dynamically:

<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <style>
      html, body { margin: 0; width: 100%; height: 100%; overflow: hidden; background: #000; }
      canvas { display: block; width: 100%; height: 100%; outline: none; }
    </style>
  </head>
  <body>
    <canvas id="canvas" oncontextmenu="event.preventDefault()"></canvas>
    <script>
      window.Module = { canvas: document.getElementById("canvas") };
      const s = document.createElement("script");
      s.src = "./game.js";
      document.body.appendChild(s);
    </script>
  </body>
</html>

wavedash.toml

game_id = "YOUR_GAME_ID_HERE"
upload_dir = "./build"

Other SDK features

Add EM_JS wrappers for whatever else you need. UTF8ToString converts C strings to JavaScript:

EM_JS(void, wavedash_submit_score, (const char* id, int score), {
  Wavedash.uploadLeaderboardScore(UTF8ToString(id), score, true);
});

See the SDK reference for the full API.