Guides

Embedding & the score bridge

AdminUpdated Sep 22, 2026

Embedding & the score bridge

Any published game on Cool GPT Games can be embedded on another website with an <iframe>. The embedded player is the real game with the site chrome removed: no header, no footer, just the game and a small "Powered by Cool GPT Games" link. It can also tell the page hosting it about scores and game events through postMessage, the score bridge, so your site can react to play, for example with its own leaderboard or a "play again" button.

This guide covers the embed snippet, what does and doesn't work inside an embed, the bridge messages, and how to handle them securely.

The embed URL

https://coolgptgames.com/embed/<slug>

<slug> is the game's slug, the last part of its page URL https://coolgptgames.com/game/<slug>. Every game page has a Share → Embed option that gives you a ready-made snippet.

The snippet

This is the snippet the game page gives you:

<iframe
  src="https://coolgptgames.com/embed/space-miner-3fa2c1"
  width="800"
  height="600"
  style="border:0;max-width:100%"
  allow="fullscreen; autoplay; gamepad"
  allowfullscreen
></iframe>

Attribute

Why

src

The embed URL for the game

width / height

Any size you like. The game fills the frame. Pick a size that suits the game's aspect ratio.

style="border:0;max-width:100%"

No border, and the frame shrinks on narrow screens

allow="fullscreen; autoplay; gamepad"

Passes these browser permissions through to the game. Without them the game's fullscreen button, audio autoplay and gamepad support won't work in the embed.

allowfullscreen

Older-browser equivalent of allow="fullscreen"

For a responsive embed that keeps its aspect ratio:

<div style="position:relative;width:100%;aspect-ratio:4/3">
  <iframe
    src="https://coolgptgames.com/embed/space-miner-3fa2c1"
    style="position:absolute;inset:0;width:100%;height:100%;border:0"
    allow="fullscreen; autoplay; gamepad"
    allowfullscreen
  ></iframe>
</div>

There are no query-string options for the embed URL. Size, placement and permissions are all controlled by your <iframe>.

Requirements for the host page

  • Serve your page over HTTPS. The game inside the embed may only be framed by https: pages, so on a plain http: page the embed shell loads but the game itself is blocked by the browser.

  • The game must be published and allow embedding. Otherwise the embed shows a message instead of the game (see Turning embedding on or off).

Turning embedding on or off

Embedding is on by default for every game. As the creator you can turn it off:

  • In the dashboard: open the game, edit its details, and untick Allow embedding on other sites.

  • Through the API: POST /v1/games/:id with { "embeddable": false } (see Games & uploads API).

When embedding is off:

  • https://coolgptgames.com/embed/<slug> shows "Embedding is disabled for this game." with a link to play on Cool GPT Games.

  • The game build itself refuses to load inside any page other than Cool GPT Games.

  • The Embed option disappears from the game's share menu.

A game that isn't currently playable (for example unpublished or delisted) shows "This game isn't playable right now." in the embed.

What works in an embed

Embedded games play as guests. Browsers keep a third-party iframe's storage separate from the same site opened directly, so the embed can't see a player's Cool GPT Games sign-in, and the embed has no sign-in button.

Feature

In an embed

Gameplay, input, audio, fullscreen, gamepad

Works, as long as you pass the allow permissions

The score bridge (below)

Works

Remote config (GameSDK.getConfig)

Works

Local saves (GameSDK.saveData / loadData)

Works as guest saves in the browser's storage for your site. They aren't synced to an account.

Multiplayer rooms

Works as for any guest

Sign-in

Not available. Players see the game's guest prompts.

XP, levels, achievements

Not recorded (guest). The game gets its normal guest responses.

Cloud saves

Not available (guest)

Reading the game's global leaderboard (GameSDK.social.getLeaderboard)

Works (friends leaderboards are empty for guests)

Score submission

Scores aren't recorded on Cool GPT Games (guest). They are still forwarded to your page through the bridge.

Tournaments

Listing tournaments and standings works; joining and competing need a signed-in player

Ranked play, in-game purchases

Not available (they need a signed-in player)

Ads

Only non-personalised (contextual) ads. Cool GPT Games' consent banner doesn't appear inside embeds, so no personalised ads are ever served there.

Games need no changes to work in an embed. A game written with the GameSDK already handles guests. See How games run, Saves & cloud progress and Ads & rewards for how guest mode behaves.

The score bridge

When a game running in an embed reports lifecycle events or scores through the GameSDK, the embed forwards a copy of each event to your page (the parent window) with window.postMessage. Your page listens for message events.

Message format

Every bridge message is a plain object:

{
  source: "coolgptgames";   // always this value
  v: 1;                     // bridge protocol version
  type: "ready" | "gamestart" | "score" | "gameover";
  game: string;             // the game's slug
  score?: number;           // on "score", and on "gameover" when the game passed one
}

Message types

type

Sent when the game calls

score

Repeats?

ready

GameSDK.ready(), meaning the game has loaded and can be played

No

Yes: sent, then re-sent after about 0.3 s and about 1 s

gamestart

GameSDK.start(), meaning a round or run has begun

No

Yes: sent, then re-sent after about 0.3 s and about 1 s

score

GameSDK.submitScore(score) with a numeric score, or GameSDK.replay.submit(score)

Yes

No: sent exactly once per call

gameover

GameSDK.gameOver(score?)

Only if the game passed a number

No: sent exactly once per call

Notes:

  • ready and gamestart are repeated so a page whose listener attaches a moment late still hears them. Treat them as idempotent state signals: "the game is ready" or "a run is in progress", not events to count.

  • score and gameover aren't repeated, so you can count them.

  • A game may call ready() or start() more than once (for example start() at the beginning of every round), and you get a message each time.

  • A game that submits scores through the replay-verification path (see Scores, leaderboards & anti-cheat) produces a score message for each GameSDK.replay.submit(score) call, just like submitScore. The value is the score the game reported, before any server-side verification.

  • Games that don't use the GameSDK produce no bridge messages.

  • Only these four types and fields cross the bridge. The player's identity, sign-in token and handle are never sent to your page.

Listening

Attach your listener before the iframe loads, so you don't miss the first ready. Check both the origin and the source:

<script>
  // Registered before the iframe below is parsed, so the first "ready" can't be missed.
  let best = 0;

  window.addEventListener("message", (event) => {
    // 1. Only accept messages from Cool GPT Games…
    if (event.origin !== "https://coolgptgames.com") return;
    // 2. …and only from this particular iframe (matters if you embed several games).
    const frame = document.getElementById("cgg-game");
    if (!frame || event.source !== frame.contentWindow) return;

    const msg = event.data;
    if (!msg || msg.source !== "coolgptgames" || msg.v !== 1) return;

    switch (msg.type) {
      case "ready":
        document.body.classList.add("game-ready");
        break;
      case "gamestart":
        document.body.classList.add("game-running");
        break;
      case "score":
        if (typeof msg.score === "number" && msg.score > best) {
          best = msg.score;
          document.getElementById("best").textContent = String(best);
        }
        break;
      case "gameover":
        document.body.classList.remove("game-running");
        showPlayAgain(typeof msg.score === "number" ? msg.score : null); // your own function
        break;
    }
  });
</script>

<p>Best score: <span id="best">0</span></p>
<iframe id="cgg-game"
  src="https://coolgptgames.com/embed/space-miner-3fa2c1"
  width="800" height="600" style="border:0;max-width:100%"
  allow="fullscreen; autoplay; gamepad" allowfullscreen></iframe>

For a script-inserted iframe, add the listener first and only then set the iframe's src or append it to the page.

Several games on one page

Each message carries the game's slug in game, and event.source tells you which iframe sent it. Compare event.source against each iframe's contentWindow to route messages to the right widget.

Security

  • Always check event.origin === "https://coolgptgames.com". Any window can postMessage your page. Without the check, another frame or a popup could send fake score events.

  • Also check event.source against your iframe's contentWindow, and data.source === "coolgptgames".

  • Bridge scores are unverified. They are what the game reported in the player's browser, which the player controls. They haven't been through Cool GPT Games' anti-cheat, and for guests they aren't recorded server-side at all. Use them for fun on-page features (a "best score" badge, a local high-score table, a "play again" prompt). Don't award prizes, money, credits or anything of value based on bridge messages alone. - Messages are one-way. The bridge sends from the embed to your page only. Nothing your page posts into the iframe is read, and your page can't control the game or reach the player's account. - The game is sandboxed. The game runs in a locked-down frame inside the embed, on a separate origin, and can't script your page. You don't need to add a sandbox attribute to your own <iframe>. If you do, include at least allow-scripts allow-same-origin allow-popups, or the embed won't work.

  • Target origin. The embed posts with target origin "*" because it can't know your page's origin in advance. That's why only non-sensitive data is ever sent.

Troubleshooting

Symptom

Likely cause

"Embedding is disabled for this game."

The creator turned embedding off

"This game isn't playable right now."

The game isn't published, or has been delisted

Frame loads but the game area stays blank

Your page is served over http:; serve it over HTTPS

No fullscreen, no sound until click, no gamepad

Missing allow="fullscreen; autoplay; gamepad" on your iframe

No bridge messages at all

Listener attached after the iframe loaded (you missed ready, but later messages should still arrive); origin check has a typo such as www. or a trailing slash; or the game doesn't use the GameSDK

score never arrives

The game calls neither GameSDK.submitScore nor GameSDK.replay.submit

See also

Was this page helpful?
Embedding & the score bridge