GameReels SDK — Concepts & API
Everything the GameReels SDK can send and receive, and the rules the host enforces. The whole surface fits on this page on purpose.
Lifecycle events (app → game)
| Event | When | What you must do |
|---|---|---|
appActive | Your game became the visible reel | Resume the loop, audio and timers. First gameplay start belongs here — never on load. |
appHidden | A swipe started or you left the screen | Pause everything: rAF work, timers, audio, physics. Save if you have unsaved progress. |
GameReels.visible reads the current state synchronously. Your game boots hidden — the feed warm-loads it off screen, so the first appActive can arrive seconds after load.
ready() — your init window
Call GameReels.ready() exactly once, the moment gameplay could start. Until you call it, an off-screen boot keeps running so you can finish downloads and initialization; after it, the host freezes the game to save battery until the player arrives. The budget is bounded: about 8 seconds after page load the host freezes you regardless.
ready() has no effect on the loading cover. The app hides its spinner/trailer when it observes your first painted frame — you cannot show earlier by calling ready() sooner, and you cannot be held covered by a missing call. Paint a branded boot screen as your first frame, not a black canvas.Gameplay events (game → app)
Optional telemetry that will power your in-portal analytics (level funnels, win/lose rates) — the ingest pipeline is still being built, so today you verify these live in the preview's event log rather than on a dashboard. Instrumenting now means your game has history the day it lands. Claims, not commands: they never move rankings or money directly, so send them honestly and freely within the limits.
GameReels.levelStart(3);
GameReels.levelEnd(3, 'win', { ms: 41200 }); // 'win' | 'lose' | 'quit'
GameReels.score(1250);
GameReels.reward('currency', 'coin', 25); // 'currency' | 'item'
GameReels.reward('item', 'sword_2');
GameReels.custom('boss_phase', { n: 2 });
GameReels.adBreak(); // see belowadBreak(kind?) marks a natural pause point — level end, game-over screen — where the app may later place an ad. It is recorded only for now; marking your break points today means your game is ready when ad revenue sharing ships.
Every call is fire-and-forget: returns a boolean (accepted or dropped), never throws, never blocks a frame, and is a safe no-op outside Flickcade. With no host at all (your own site, a local build) the bridge delivers appActiveitself after a short handshake window, so the same "start on appActive" code runs everywhere.
Utilities
GameReels.isHost(); // true inside the Flickcade app, false on your own site GameReels.version; // bridge version string, currently '1.1.2' GameReels.emit(type, payload); // low-level; accepts known event names only
isHost() is the honest way to branch on environment — gate a debug overlay, skip your own pause UI when the app owns it. Prefer the named helpers above over emit(): it enforces the same whitelist, so it buys nothing except a typo you cannot see.
Device capabilities
Your game may be any kind of game. On a player's phone it can use the camera, the microphone and the location— the app asks the player once, per game, naming your game, and then the phone's own permission dialog follows if the app does not hold that permission yet. A "no" at either step rejects your request the standard way (NotAllowedError from getUserMedia, PERMISSION_DENIED from the Geolocation API), so handle those exactly as you would on the web. Motion and orientation events, fullscreen-sized rendering, autoplay, gamepads, wake lock and WebXR need no permission at all.
A grant lasts only while your game is on screen. The moment the player swipes to another game, leaves the feed tab or backgrounds the app, every capture track is stopped and every location watch is cleared — and a request made while you are off screen is rejected. So never assume a stream from a previous session is still live: request what you need again on appActive (a player who already said yes to your game is not asked again). Request the camera or the microphone from a tap, not on load, and only when your gameplay needs it — a game that asks before the player has done anything is a game the player says no to.
Payments, credentials, USB / Bluetooth / serial devices, screen capture, clipboard reads and ad-tech APIs are denied to every game, and to every frame inside it. A frame from another origin inside your game gets no capability unless your own markup delegates it with allow.
Limits
| Limit | Value | On breach |
|---|---|---|
| Event name | [a-zA-Z][A-Za-z0-9_]{0,31} | dropped |
| Payload | flat object · ≤ 8 keys · keys/strings ≤ 64 chars · numbers finite · ≤ 512 serialized UTF-8 bytes | dropped |
| Rate | 10 events/s sustained, burst 30 | dropped |
| Per page load | 1000 gameplay events (drop notices have their own bound) | dropped |
Drops are counted, never silent — the preview's event log shows a dropped row with the running count. ready() and lifecycle delivery are never rate-limited.
Versioning promise
The app injects its own copy of the SDK, which always wins over the one in your bundle — you will never be asked to re-publish a game because the SDK changed. Published names are permanent: events and calls are only ever added, never renamed or removed.