Poki SDK: gameplayStart, gameplayStop and commercial breaks

Fire Poki SDK events in the order Poki's review checks: loading, gameplay start and stop, commercial and rewarded breaks, muting, with vanilla JS and Phaser 3 code.

Published
Sources checked
Versions
Poki SDK (HTML5, v2 script), Phaser 3.90.0

Poki’s SDK has few calls, but Poki’s review checks the order in which you fire them. This guide covers the five event moments Poki defines, the order rules from its requirements page, and a tested adapter that keeps your game code free of Poki-specific calls. Examples use plain JavaScript and Phaser 3.90.0; Poki’s docs were checked on 2026-09-30.

Load the SDK and initialise it

The HTML5 SDK guide asks for this tag inside <head>:

<script src="https://game-cdn.poki.com/scripts/v2/poki-sdk.js"></script>

Then call PokiSDK.init(), which returns a promise. Poki’s own example continues into the game in both the then and the catch branch: if initialisation fails, the game must still load. Its requirements also say the game must stay playable with an ad blocker active.

The five events and their order

The SDK overview lists five calls:

  • gameLoadingFinished() when loading completes. It powers the conversion-to-play metric.
  • gameplayStart() when the player starts interacting: first input, level start, unpause.
  • gameplayStop() when gameplay halts: pause, level complete, death, quit to menu.
  • commercialBreak() at a natural stop when the player is about to go back into gameplay.
  • rewardedBreak() when the player explicitly chooses to watch an ad for a reward.

The overview also gives the order for common moments. Paraphrased:

Moment Sequence
Startup gameLoadingFinished() then gameplayStart() on first input
Next level gameplayStop(), commercialBreak(), gameplayStart()
Death and restart gameplayStop(), commercialBreak(), gameplayStart()
Death and revive gameplayStop(), rewardedBreak(), gameplayStart()

The requirements page turns this into checks that Poki’s team applies to every game:

  • the same event must not fire twice in a row (no gameplayStart() after gameplayStart());
  • gameplayStart() fires on the player’s first input, not on load;
  • gameplayStop() fires on every interruption, including cutscenes;
  • no SDK events may fire during an ad;
  • commercialBreak() fires only when leaving a pause and heading back into gameplay. Leaving gameplay for a level-select screen is the wrong moment.

Commercial and rewarded breaks

Both break calls take an optional callback that runs just before an ad plays and return a promise that resolves when the break is over. Poki’s example pauses audio in that callback and resumes it in then, with a warning that the callback might not run at all (no ad was shown). So always resume in then, never only in response to the callback.

rewardedBreak() resolves with a boolean: grant the reward only when it is true. Poki’s docs add that a rewarded break resets its ad timer, so players do not see a commercial break right after one.

The requirements also cover the reward UI. Among other rules, a normal “continue” option must always be shown next to or above the rewarded one, be at least as large, and the rewarded button must carry a 🎬 icon and must not be green. Do not build your own ad timers, and do not show your own “ad blocked” messages; Poki handles that.

During any break, mute audio and disable keyboard input. The HTML5 guide also recommends stopping arrow keys and space from scrolling the page, because on Poki the game sits inside a longer page:

window.addEventListener('keydown', (ev) => {
  if (['ArrowDown', 'ArrowUp', ' '].includes(ev.key)) ev.preventDefault();
});
window.addEventListener('wheel', (ev) => ev.preventDefault(), { passive: false });

The adapter

The example game on this site calls a portal adapter, never PokiSDK directly. The adapter makes gameplay events idempotent (the “never twice in a row” rule), stops gameplay before any break, and resolves rewards only on success. It has the same interface as the CrazyGames adapter and the Ad Placement API adapter.

// file: examples/lib/adapters/poki.js
// Poki SDK adapter.
// Docs: https://developers.poki.com/guide/sdk-html5 (checked 2026-09-30)
// index.html must load https://game-cdn.poki.com/scripts/v2/poki-sdk.js in <head>.
import { gameplayGuard } from './types.js';
import { createNoopAdapter } from './noop.js';

/**
 * @param {any} [sdk] window.PokiSDK (injectable for tests)
 * @returns {Promise<import('./types.js').PortalAdapter>}
 */
export async function createPokiAdapter(sdk = globalThis.PokiSDK) {
  if (!sdk) return createNoopAdapter('Poki SDK script not loaded');
  try {
    await sdk.init();
  } catch {
    // Poki's docs: if init fails, load the game anyway.
  }

  const guard = gameplayGuard(
    () => sdk.gameplayStart(),
    () => sdk.gameplayStop(),
  );

  return {
    name: 'poki',
    // The HTML5 SDK has no "loading started" event; only the end of loading is reported.
    loadingStart() {},
    loadingStop: () => sdk.gameLoadingFinished(),
    ...guard,
    async commercialBreak(hooks) {
      guard.gameplayStop();
      hooks.pause();
      // The callback runs only if an ad plays; resume in .then() regardless.
      await sdk.commercialBreak(() => hooks.pause());
      hooks.resume();
    },
    rewardedBreak(hooks) {
      return new Promise((resolve) => {
        hooks.showPrompt(
          async () => {
            guard.gameplayStop();
            hooks.pause();
            const success = await sdk.rewardedBreak(() => hooks.pause());
            hooks.resume();
            resolve(Boolean(success));
          },
          () => resolve(false),
        );
      });
    },
    // Poki's HTML5 SDK does not document a portal-level mute setting.
    onMuteRequest() {},
  };
}

gameplayGuard is a small helper in examples/lib/adapters/types.js that ignores a start while already playing and a stop while already stopped.

Wiring it into the game

In the vanilla example, the level-complete screen’s button calls the adapter before the next level begins, which gives exactly the “next level” sequence above:

// From examples/lib/tap-game.js (level complete)
onClick: async () => {
  hideOverlay();
  // A natural break the player chose to continue from: offer an ad before gameplay resumes.
  await adapter.commercialBreak({ name: `level-${state.level}-complete`, ...hooks });
  state.level += 1;
  startLevel(); // calls adapter.gameplayStart()
},

gameplayStop() was already sent when the level ended, and the adapter’s guard makes the second call inside commercialBreak() a no-op. The “revive” path uses rewardedBreak(): when time runs out, the game shows “No thanks” first and “🎬 Watch an ad for 6 more seconds” second, and only adds time if the promise resolves to true.

The entry point for the vanilla build:

// file: examples/vanilla-poki/main.js
// Poki SDK example, vanilla JS. Build: npm run examples:build, then serve examples/dist/ on localhost.
import { createPokiAdapter } from '../lib/adapters/poki.js';
import { startTapGame } from '../lib/tap-game.js';

createPokiAdapter().then((adapter) => {
  adapter.loadingStart();
  // Load your assets here. This example has none, so loading ends immediately.
  adapter.loadingStop();
  startTapGame({
    canvas: /** @type {HTMLCanvasElement} */ (document.getElementById('game')),
    overlay: /** @type {HTMLElement} */ (document.getElementById('overlay')),
    adapter,
  });
  console.info(`[example] portal adapter: ${adapter.name}`);
});

Phaser 3: plugin or adapter

Poki’s Phaser 3 page recommends its official plugin, published on npm as @poki/phaser-3 (source on GitHub). According to the plugin’s documentation it loads the SDK, fires gameLoadingFinished when your loading scene finishes, fires the gameplay events when your gameplay scene starts and stops, requests a commercial break before gameplayStart, and disables input and audio during ads. You tell it the keys of your loading and gameplay scenes in the Phaser config.

If Poki is your only target, the plugin is the shorter path. The adapter approach in this guide is for games that also ship to CrazyGames, GameDistribution or your own site with the Ad Placement API: the Phaser scenes stay the same and only main.js changes:

// file: examples/phaser-poki/main.js
// Poki SDK example, Phaser 3. Build: npm run examples:build, then serve examples/dist/ on localhost.
import { createPokiAdapter } from '../lib/adapters/poki.js';
import { createPhaserGame } from '../lib/phaser-game.js';

createPokiAdapter().then((adapter) => {
  createPhaserGame(adapter, 'game');
  console.info(`[example] portal adapter: ${adapter.name}`);
});

Test in the Poki Inspector

Poki’s HTML5 guide ends with the same step for every engine: upload your build to the Poki Inspector and check the event flow there before requesting a review. Before that, you can run the Portal Readiness Checker on your zip. It reports which Poki calls appear in your code, flags other portals’ SDKs, and lists hard-coded external URLs. That matters on Poki: its requirements say all external requests are blocked by default, so fonts, assets and libraries must be inside the build.

What to check before you submit

  • The SDK tag is in <head> and the game loads even if PokiSDK.init() rejects.
  • gameLoadingFinished() fires once, when loading is done.
  • gameplayStart() fires on the first input, not on load, and never twice in a row.
  • gameplayStop() fires on pause, menus, level end, death and cutscenes.
  • commercialBreak() sits between gameplayStop() and the next gameplayStart() when the player heads back into play.
  • Audio and keyboard input are off during breaks; audio resumes in the promise’s then.
  • Rewards are granted only when rewardedBreak() resolves to true, and the reward UI follows Poki’s layout rules.
  • No other ad SDKs, no external requests, no outgoing links except through PokiSDK.openExternalLink().
  • localStorage access is wrapped in try/catch; Poki notes that incognito mode restricts it. See saving progress safely.

Limits of this guide

This guide covers the core HTML5 SDK events. It does not cover Poki’s measure() game events, shareable URLs, user accounts, the data store or engine plugins other than Phaser’s. The adapter has been unit-tested against a mock that records calls; it has not been run on poki.com.

Questions

When should gameplayStart() fire on Poki?
On the player's first input and every return to gameplay, not on page load. Poki's requirements also say it must never fire twice in a row without a gameplayStop() in between.
Does every commercialBreak() show an ad?
No. Poki's system decides whether a player is ready for another ad, and its docs encourage you to signal as many natural break opportunities as possible. Do not add your own ad timers.
Should I use Poki's Phaser plugin or call the SDK myself?
Poki's Phaser 3 page recommends its official plugin, which fires loading and gameplay events and requests commercial breaks automatically. Calling the SDK through a small adapter, as in this guide, is useful when the same game also ships to other portals.

Sources

  1. Poki for Developers: PokiSDK HTML5developers.poki.com, accessed
  2. Poki for Developers: SDK overview and eventsdevelopers.poki.com, accessed
  3. Poki for Developers: Requirementsdevelopers.poki.com, accessed
  4. Poki for Developers: PokiSDK Phaser 3developers.poki.com, accessed
  5. Poki Phaser 3 plugin (GitHub, poki/phaser-plugin)github.com, accessed