Search documentation

Find pages, sections, and content across all docs.

WavedashDocs

GameMaker

Publish GameMaker projects to the web via the GX.games (Opera GX) WebAssembly export.

View example project on GitHub Playtest the example project

GameMaker ships to the web via its GX.games (Opera GX) export target, which produces a WebAssembly runner plus a flat set of static files. Wavedash hosts those files directly.

Export your game

First, set your build target. In the top right of the GameMaker workspace, click the Target Device dropdown to open the Target Platform manager, then select GX.games as the Platform and GMS2 VM as the Output. Click Apply.

Then in the IDE: Build → Create Executable. This produces a .zip containing:

  • index.html
  • runner.js, runner.wasm
  • runner.data, audio-worklet.js
  • game.unx (your packed game)
  • runner.json

Unzip into build/ and point upload_dir at it.

SDK integration

GameMaker reaches JavaScript through an extension. Create one in your project (e.g. ext_wavedash) with a single .js file that wraps Wavedash:

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.

// extensions/ext_wavedash/ext_wavedash.js
function wavedash_js_init() {
  const Wavedash = window.Wavedash;
  if (Wavedash) Wavedash.init({ debug: true });
  return 1.0;
}

function wavedash_js_update_progress(progress) {
  const Wavedash = window.Wavedash;
  if (Wavedash) Wavedash.updateLoadProgressZeroToOne(progress);
  return 1.0;
}

Register each function in the extension editor — make sure External Name matches the JS function name, Argument Count matches the number of arguments, and the extension is marked for the HTML5 / Opera GX target.

Then wrap them in a GML script so the rest of your code doesn't touch the extension directly:

// scripts/scr_wavedash/scr_wavedash.gml
function wavedash_init() {
    wavedash_js_init();
}

function wavedash_update_progress(_progress) {
    wavedash_js_update_progress(_progress);
}

Load progress and init

Wavedash expects two calls: a progress update while your game loads, and a single init once the player can interact. Put them in your root controller's Create event:

// obj_game Create event
wavedash_update_progress(1);
wavedash_init();

wavedash.toml

game_id = "YOUR_GAME_ID_HERE"
upload_dir = "./build"
entrypoint = "index.html"

Filling the viewport

The GX.games export's generated index.html defines an ensureAspectRatio() function that fits the canvas within both window.innerWidth and window.innerHeight, so on iframes that are short relative to your room's aspect, the canvas is bound by height and leaves horizontal margins. There's no project option to change this — see GameMaker-Bugs #5498 — but ensureAspectRatio is a global, so you can replace it from your extension JS:

// extensions/ext_wavedash/ext_wavedash.js
(function () {
  window.ensureAspectRatio = function () {
    if (!window.startingAspect) return;
    const c = document.getElementById("canvas");
    if (!c) return;
    c.classList.add("active");
    c.style.width = window.innerWidth + "px";
    c.style.height = (window.innerWidth / window.startingAspect) + "px";
  };
})();

The IIFE runs when the runner loads, after the generated index.html has defined ensureAspectRatio as a global. The runner's own ResizeObserver then calls our version on every viewport change.

For the render inside the canvas to fill the full canvas (not just a corner of it):

  1. Game Options → GX.games → Scaling: Full scale (option_operagx_scale: 1). Fixed scale keeps the render at native resolution, leaving black space; Full scale stretches the room to the canvas drawing buffer.
  2. Room size must match your game's playfield. If your draw code uses constants like FIELD_W = 960; FIELD_H = 540 but your room is 1366×768, GameMaker draws the game in the upper-left quadrant of the room and stretches that whole room (game + empty space) to the canvas — leaving most of the canvas black. Match the room dimensions to what you actually draw.

Known issues

show_message and alerts. Wavedash hosts games in a sandboxed iframe without allow-modals. GameMaker's show_message compiles to alert(), which silently fails. Remove all show_message calls or replace them with in-game UI.

Ad and mobile extensions. If your project includes Google Mobile Ads, IAP, or other mobile-only extensions, make sure they exit early or are stripped for web builds. Leftover ad snippets in the exported index.html can cause console errors.

Debug output bar. The GX.games export template includes a <div id="output-container"> at the bottom of the page with "Toggle Stats", "Copy Share Url", and "Log State" buttons plus a debug textarea. There's no GameMaker option to disable it. Hide it from your extension JS:

const hideDebugUI = () => {
  const el = document.getElementById("output-container");
  if (el) el.hidden = true;
};
if (document.readyState === "loading") {
  document.addEventListener("DOMContentLoaded", hideDebugUI);
} else {
  hideDebugUI();
}

HTML5 target. The legacy HTML5 export ships pure JavaScript with no WebAssembly runner (#5498) — use GX.games even if you're not publishing to Opera GX. WebGL is available on both targets.