Developer SDK

Connect your game to scores, leaderboards, pause and ad breaks. There is no library to install: it is a handful of postMessage calls.

Overview

Every game on YoHU.io runs inside an <iframe> on its game page. The page and the game talk with window.postMessage. Each message is a plain object with a source and a type:

  • Messages from your game use source: 'yohu-game'.
  • Messages from the portal use source: 'yohu-portal'.

Anything with another source is ignored, so the bridge cannot clash with your own messages. All calls are optional: a game that sends nothing still plays fine, it just will not record scores.

Embedding

The iframe fills the game area and, in full screen or on phones, the whole screen. It is created with these permissions:

Iframe the portal creates
<iframe src="your-game/index.html?autoplay=1"
        allow="autoplay; fullscreen; gamepad; screen-wake-lock; accelerometer; gyroscope"
        allowfullscreen></iframe>
  • Size your canvas to the iframe (100vw × 100vh) and handle resize and orientation changes. Game areas range from about 360 × 450 on phones to full HD.
  • Do not navigate the top window or open pop-ups. Links to your own site are fine on your credits screen with target="_blank".
  • We can host your build (a zip of static files) or embed it from your own https:// URL.

URL flags

For games we host, the portal adds query parameters to your game's URL. Read them with URLSearchParams:

FlagWhenWhat your game should do
?autoplay=1The player pressed Play on the game page.Skip the title screen and start a run right away.
?demo=1&muted=1Live preview while the mouse hovers a game card (featured in-house games).Play an attract loop by itself, keep sound off, send no scores.

Messages from your game

typeFieldsWhat happens
gameoverscore (integer, required), best, newBestThe score is saved as the player's personal best. Registered players also enter the leaderboards and the weekly cup. The page shows “New personal best!” when it is one.
adbreaknoneA natural pause between rounds or levels. See Ad breaks.
pause / resumenoneOptional status updates when your own pause menu opens or closes. Reserved for future features.
Send a score when a run ends
window.parent.postMessage({ source: 'yohu-game', type: 'gameover', score: Math.floor(score) }, '*');

Messages from the portal

typeFieldsWhat your game should do
pausenonePause. Sent when the player closes the full-screen view on a phone.
adbreak-acknoneAn ad is about to show. Stay paused and keep sound off.
adbreak-donenoneContinue the game.
soundon (boolean)Reserved. Mute or unmute if you support it.

Ad breaks

Post adbreak at natural pauses only: between levels or after a game over, never in the middle of play. The portal decides whether to show an ad (at most every third break, and only when ads are switched on) and answers:

  1. No ad: it replies adbreak-done straight away.
  2. Ad: it replies adbreak-ack, shows a short ad over the game, then sends adbreak-done.

Wait about 400 ms for an answer. If nothing arrives (for example when your game runs on its own site), carry on. Never wait longer than 20 seconds in total.

Ad breaks you trigger count toward your revenue share.

Scores & leaderboards

  • Send one gameover per finished run, with the final score as a whole number from 0 to 1,000,000,000. Do not send scores mid-run.
  • Up to 12 scores per minute are accepted from a player; extra ones are dropped.
  • Guests get personal bests on their device. When they log in, those scores move to their account.
  • Leaderboard pages and weekly cups currently feature in-house Originals. Partner games keep personal bests and can join the boards on request.

Example bridge

Drop this into any engine. It works the same when your game runs on its own, outside the portal.

yohu-bridge.js
const YoHU = {
  embedded: window.parent !== window,
  post(type, data = {}) {
    if (this.embedded) window.parent.postMessage({ source: 'yohu-game', type, ...data }, '*');
  },
  gameOver(score) { this.post('gameover', { score: Math.max(0, Math.floor(score)) }); },
  adBreak(next) {
    if (!this.embedded) return next();
    let acked = false, done = false;
    const finish = () => { if (done) return; done = true; removeEventListener('message', onMsg); next(); };
    const onMsg = (e) => {
      const d = e.data;
      if (!d || d.source !== 'yohu-portal') return;
      if (d.type === 'adbreak-ack') acked = true;
      if (d.type === 'adbreak-done') finish();
    };
    addEventListener('message', onMsg);
    this.post('adbreak');
    setTimeout(() => { if (!acked) finish(); }, 400);  // no answer: carry on
    setTimeout(finish, 20000);                     // safety net
  },
};

// The portal asks the game to pause
addEventListener('message', (e) => {
  if (e.data?.source === 'yohu-portal' && e.data.type === 'pause') game.pause();
});

// Start straight away after the player pressed Play
if (new URLSearchParams(location.search).has('autoplay')) game.start();

// When a run ends, and between levels
YoHU.gameOver(score);
YoHU.adBreak(() => game.nextLevel());

Testing checklist

  • Plays inside an iframe at 360 × 640 (phone, portrait) and 1920 × 1080 (desktop).
  • Touch and keyboard or mouse both work; nothing needs a right click on phones.
  • Pauses on visibilitychange and when it receives pause.
  • Sound starts only after the first tap or key press (browser autoplay rules).
  • gameover is posted exactly once per run, with a whole number.
  • No errors in the browser console, no requests to unexpected domains.

Ready? Submit your game and mention in the notes that it uses the bridge.