SDK Event Order Checker for Poki, CrazyGames and YouTube Playables

Paste a log of your game's SDK calls and check it against the order rules Poki, CrazyGames and YouTube Playables publish. Runs in your browser.

Published
Sources checked
SDK

Paste the lines printed by the recorder below, or type one event per line (for examplegameplayStart). Nothing leaves this page.

The recorder (debug builds only)

Load it after the portal SDK’s <script> tag and before your game code, callinstallSdkRecorder(), play through a level, then run copySdkLog() in the console and paste the result above. It wraps the documented SDK calls and logs each one with a timestamp. Remove it before you submit.

// SDK event recorder for the SDK Event Order Checker (debug builds only; remove before submitting).
// Load it AFTER the portal SDK's <script> tag and BEFORE your game code. It wraps the documented SDK
// calls of Poki, CrazyGames v3 and YouTube Playables, prints one line per call, and keeps the lines in
// window.__sdkLog. Copy them with copySdkLog() and paste them into the checker.
export function installSdkRecorder(win = window) {
  const t0 = performance.now();
  /** @type {string[]} */
  const log = (win.__sdkLog = []);
  const rec = (/** @type {string} */ sdk, /** @type {string} */ name) => {
    const line = `[sdk-log] +${Math.round(performance.now() - t0)}ms ${sdk} ${name}`;
    log.push(line);
    console.log(line);
  };

  // Records the player's first input, so the checker can tell "on load" from "on first input".
  const firstInput = () => rec('input', 'first-input');
  win.addEventListener('pointerdown', firstInput, { once: true, capture: true });
  win.addEventListener('keydown', firstInput, { once: true, capture: true });

  /** Wraps obj[method]; logs the call, and `name:done` (+ result) when a returned promise settles. */
  const wrap = (/** @type {any} */ obj, /** @type {string} */ method, /** @type {string} */ sdk, /** @type {string} */ name = method, withResult = false) => {
    if (!obj || typeof obj[method] !== 'function') return;
    const original = obj[method];
    obj[method] = function (/** @type {any[]} */ ...args) {
      rec(sdk, name);
      const out = original.apply(this, args);
      if (out && typeof out.then === 'function') {
        out.then(
          (/** @type {unknown} */ v) => rec(sdk, withResult ? `${name}:done:${Boolean(v)}` : `${name}:done`),
          () => rec(sdk, `${name}:error`),
        );
      }
      return out;
    };
  };

  const poki = win.PokiSDK;
  if (poki) {
    for (const m of ['init', 'gameLoadingFinished', 'gameplayStart', 'gameplayStop']) wrap(poki, m, 'poki');
    wrap(poki, 'commercialBreak', 'poki');
    wrap(poki, 'rewardedBreak', 'poki', 'rewardedBreak', true);
  }

  const cg = win.CrazyGames && win.CrazyGames.SDK;
  if (cg) {
    wrap(cg, 'init', 'crazygames');
    for (const m of ['loadingStart', 'loadingStop', 'gameplayStart', 'gameplayStop']) wrap(cg.game, m, 'crazygames');
    if (cg.ad && typeof cg.ad.requestAd === 'function') {
      const requestAd = cg.ad.requestAd;
      cg.ad.requestAd = function (/** @type {string} */ type, /** @type {any} */ callbacks = {}) {
        rec('crazygames', `requestAd:${type}`);
        const cb = (/** @type {string} */ k) => (/** @type {any[]} */ ...a) => {
          rec('crazygames', k);
          return callbacks[k]?.(...a);
        };
        return requestAd.call(this, type, { ...callbacks, adStarted: cb('adStarted'), adFinished: cb('adFinished'), adError: cb('adError') });
      };
    }
  }

  const yt = win.ytgame;
  if (yt) {
    for (const m of ['firstFrameReady', 'gameReady', 'loadData', 'saveData']) wrap(yt.game, m, 'playables');
    wrap(yt.ads, 'requestInterstitialAd', 'playables');
    wrap(yt.ads, 'requestRewardedAd', 'playables', 'requestRewardedAd', true);
  }

  win.copySdkLog = () => log.join('\n');
  return log;
}

Why the order of SDK calls matters

Portal SDKs measure your game through a handful of calls: when loading finished, when gameplay started and stopped, when an ad break happened. The portals use those calls for their dashboards and for ad timing, and some of them review the order explicitly. Poki’s requirements list the common mistakes: the same event fired twice in a row, gameplayStart() on page load instead of the first input, SDK events during an ad, and commercialBreak() used when leaving gameplay rather than when heading back into it. YouTube Playables certifies the order of its ready signals and cloud saves (integration requirements).

These mistakes are easy to make and hard to see, because the game looks fine while the event stream is wrong. This tool reads a log of the calls and checks it against the published rules.

How to use it

  1. Add the recorder to a debug build: load it after the portal SDK’s script tag and before your game code, and call installSdkRecorder(). It wraps the SDK methods and prints lines such as [sdk-log] +1215ms poki gameplayStart, plus one line for the player’s first tap or key press.
  2. Play through a typical session: start, finish a level, lose, continue with a reward, quit to the menu.
  3. Run copySdkLog() in the browser console, paste the result above, pick the SDK, and press Check the log.

Without the recorder, you can paste or type event names one per line (gameplayStart, commercialBreak, commercialBreak:done). The “first input” rule then shows as a note, because only the recorder logs input.

What it checks

The full list, generated from the checker’s code, is below the tool. In short:

  • Poki: no repeated gameplayStart/gameplayStop; gameLoadingFinished before the first gameplayStart; the first gameplayStart after the first input; breaks only after gameplayStop; no SDK events while a break plays; a commercial break leads back into gameplay. Sources: Poki’s requirements and order of events.
  • CrazyGames SDK v3: init() first; if you use the optional loading events, a loadingStart/loadingStop pair before gameplay; a gameplayStart exists (it ends CrazyGames’ initial-download measurement); ads requested only after gameplayStop; every ad request ends with adFinished or adError; no gameplay events while an ad plays. Sources: the game module and ad requirements.
  • YouTube Playables: firstFrameReady() before gameReady(), gameReady() called once, and loadData() completed before the first saveData().

What it cannot know

A log shows order, not context. The checker cannot tell whether a loading screen was still visible when you called gameReady(), whether your game actually paused and muted during an ad, or whether an ad was placed at a natural break. It reads one session, so code paths you did not play are not checked. CrazyGames does not forbid repeated gameplay events outright, so those are warnings rather than problems. Treat a clean log as one piece of evidence, then test in each portal’s own QA tool.

The SDK guides show the call order in context, with tested example code for each portal.

The rules it checks

Generated from the checker’s code. Each rule links to the official page it comes from.

Poki SDK

CrazyGames SDK v3

YouTube Playables SDK

Questions

Where does the log come from?
From the recorder shown on this page, which wraps the documented SDK calls in a debug build and prints one line per call. You can also type or paste event names, one per line.
Does a clean result mean my integration will pass review?
No. The checker only sees the order of calls in one play session. Reviewers also check timing, ad placement, audio and gameplay, which a log cannot show. Use it to catch ordering mistakes before you upload.
Is my log sent anywhere?
No. The log is checked by JavaScript in this page and is not uploaded or stored.

Sources

  1. Poki for Developers: Requirements (technical standards)developers.poki.com, accessed
  2. Poki for Developers: SDK overview and eventsdevelopers.poki.com, accessed
  3. CrazyGames docs: Game moduledocs.crazygames.com, accessed
  4. CrazyGames docs: Advertisement requirementsdocs.crazygames.com, accessed
  5. YouTube Playables: Integration requirementsdevelopers.google.com, accessed