Engine recipes
Drop-in setups for the most common HTML5 game stacks.
The SDK is engine-agnostic — you can wire it into anything that runs in a browser. This page collects the canonical setup for the engines GAMEE devs use most. Every recipe follows the same shape:
- Init the SDK in your boot path.
- Subscribe to
start,pause,resume,mute,unmute. - Call
gameReady()the moment the engine is up. - Translate SDK events into the engine’s lifecycle hooks.
The vanilla recipe below is the canonical baseline; the engine-specific recipes just translate steps 1–4 into the engine’s idioms.
Vanilla JS / Canvas (the baseline)#
<!doctype html>
<canvas id="c"></canvas>
<script src="/vendor/gamee-sdk/gamee-sdk.iife.js"></script>
<script>
const { gamee } = Gamee;
let running = false;
let last = 0;
async function boot() {
await gamee.init({ capabilities: [] });
gamee.on('start', () => {
running = true;
last = performance.now();
requestAnimationFrame(loop);
});
gamee.on('pause', () => {
running = false;
});
gamee.on('resume', () => {
running = true;
last = performance.now();
requestAnimationFrame(loop);
});
gamee.on('mute', () => audio.mute());
gamee.on('unmute', () => audio.unmute());
gamee.gameReady();
}
function loop(now) {
if (!running) return;
const dt = (now - last) / 1000;
last = now;
update(dt);
render();
requestAnimationFrame(loop);
}
boot();
</script>
The other recipes show the same five touchpoints inside each engine.
Phaser 3#
import { gamee } from '@gamee/sdk';
import Phaser from 'phaser';
class MainScene extends Phaser.Scene {
preload() {
/* assets */
}
create() {
/* build the world */
}
update(_t: number, dt: number) {
// Phaser passes dt in ms; divide by 1000 if you want seconds.
}
}
async function boot() {
await gamee.init({ capabilities: ['saveState'] });
const game = new Phaser.Game({
type: Phaser.AUTO,
parent: 'game',
width: window.innerWidth,
height: window.innerHeight,
scene: [MainScene],
scale: { mode: Phaser.Scale.RESIZE },
});
// Map platform events to Phaser scene control.
gamee.on('start', () => game.scene.start('MainScene'));
gamee.on('pause', () => game.scene.getScenes(true).forEach((s) => s.scene.pause()));
gamee.on('resume', () => game.scene.getScenes(false).forEach((s) => s.scene.resume()));
gamee.on('mute', () => (game.sound.mute = true));
gamee.on('unmute', () => (game.sound.mute = false));
gamee.gameReady();
}
boot();
CDN script-tag variant — same logic, no bundler:
<script src="https://cdn.jsdelivr.net/npm/phaser@3/dist/phaser.min.js"></script>
<script src="/vendor/gamee-sdk/gamee-sdk.iife.js"></script>
<script>
// Use `Gamee.gamee` and the global `Phaser`.
</script>
PixiJS#
PixiJS games drive their loop from app.ticker. Stop the ticker on pause,
start it on resume.
import { gamee } from '@gamee/sdk';
import * as PIXI from 'pixi.js';
const app = new PIXI.Application();
async function boot() {
await app.init({ resizeTo: window });
document.body.appendChild(app.canvas);
await gamee.init({ capabilities: [] });
// Build the world here.
// ...
gamee.on('start', () => app.ticker.start());
gamee.on('pause', () => app.ticker.stop());
gamee.on('resume', () => app.ticker.start());
gamee.on('mute', () => audio.mute());
gamee.on('unmute', () => audio.unmute());
gamee.gameReady();
}
boot();
Three.js#
Three has no built-in scene-pause hook — you guard the render loop yourself.
import { gamee } from '@gamee/sdk';
import * as THREE from 'three';
const renderer = new THREE.WebGLRenderer({ canvas: document.querySelector('canvas')! });
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight);
let running = false;
let last = 0;
function loop(now: number) {
if (!running) return;
const dt = (now - last) / 1000;
last = now;
// update(dt)
renderer.render(scene, camera);
requestAnimationFrame(loop);
}
async function boot() {
await gamee.init({ capabilities: [] });
gamee.on('start', () => {
running = true;
last = performance.now();
requestAnimationFrame(loop);
});
gamee.on('pause', () => {
running = false;
});
gamee.on('resume', () => {
running = true;
last = performance.now();
requestAnimationFrame(loop);
});
gamee.on('mute', () => audio.mute());
gamee.on('unmute', () => audio.unmute());
gamee.gameReady();
}
boot();
Babylon.js#
Babylon’s engine.runRenderLoop is the natural pause/resume hook.
import { gamee } from '@gamee/sdk';
import { Engine, Scene, ArcRotateCamera, Vector3 } from '@babylonjs/core';
const canvas = document.querySelector('canvas') as HTMLCanvasElement;
const engine = new Engine(canvas, true);
const scene = new Scene(engine);
new ArcRotateCamera('cam', 0, 1, 5, Vector3.Zero(), scene).attachControl(canvas, true);
const tick = () => scene.render();
async function boot() {
await gamee.init({ capabilities: [] });
gamee.on('start', () => engine.runRenderLoop(tick));
gamee.on('pause', () => engine.stopRenderLoop(tick));
gamee.on('resume', () => engine.runRenderLoop(tick));
gamee.on('mute', () => Engine.audioEngine!.setGlobalVolume(0));
gamee.on('unmute', () => Engine.audioEngine!.setGlobalVolume(1));
gamee.gameReady();
}
window.addEventListener('resize', () => engine.resize());
boot();
Construct 3#
In Construct 3, exported HTML5 projects can mount a JavaScript module via the
Scripting feature. Wire the SDK from the On start of layout script and
toggle the runtime on pause/resume.
// scripts/main.js — set "Scripts in event sheet" → On start of layout.
import { gamee } from '@gamee/sdk';
runOnStartup(async (runtime) => {
await gamee.init({ capabilities: ['saveState'] });
gamee.on('start', () => runtime.setSuspended(false));
gamee.on('pause', () => runtime.setSuspended(true));
gamee.on('resume', () => runtime.setSuspended(false));
gamee.on('mute', () => runtime.objects.Audio.setSilent(true));
gamee.on('unmute', () => runtime.objects.Audio.setSilent(false));
// Forward Construct's score system to the platform on every change:
runtime.addEventListener('tick', () => {
const score = runtime.globalVars.Score;
if (score !== last) {
gamee.updateScore({ score, playTime: runtime.gameTime, checksum: 'c3' });
last = score;
}
});
gamee.gameReady();
});
let last = 0;
If you can’t add an npm dependency to your Construct project, switch to the
IIFE script tag in index.html and use Gamee.gamee instead of the import.
Choosing your import style#
Every recipe above uses the npm import. The same code works with all three import styles documented on the install page:
- npm + ESM — what’s in the recipes.
<script>IIFE — replaceimport { gamee } from '@gamee/sdk'withconst { gamee } = Gameeand load the CDN script first.- CDN ESM — replace the import URL with
'https://esm.sh/@gamee/[email protected]'.
When in doubt, start with the IIFE — zero build setup, fastest to test against the emulator (top-right button on every page).