Skip to content

Visual & UX expectations for casino iframe games

Make your casino iframe feel at home in the host. Build around snapshots, theme and locale settings, readable controls, and motion players can dial down.

  • The main app embeds your game in an iframe with
    sandbox="allow-scripts allow-same-origin"
    (see packages/web/src/features/casino/CasinoGameFrameClient.tsx).
    Design for a single-page guest that does not navigate away or open new windows unless you intentionally handle it (some flows may be limited by sandbox).
  • The host owns wallet, Smart Vault, and signing. Your UI should assume no direct wallet access — reflect state from HostSnapshot only.

The host pushes:

  • wallet, token, balances (e.g. Smart Vault balance as string)
  • sessions — active/settled sessions with raw.gameData, raw.gameState, raw.randomness, phases
  • ui.locale, ui.theme — light | dark | system

A polished guest should respect ui.theme and ui.locale where practical (CSS variables, prefers-color-scheme when system, or reading parent theme if you mirror tokens).

Served at {gameBaseUrl}/game.manifest.json (same origin as the game). Validated by validateCasinoGameManifest in /casino-sdk (src/manifest.ts). Required fields include:

  • presentation: mode (full-iframe | embedded), hostPanels toggles (whether the host shows open-session / history / status chrome — your layout can leave room accordingly).
  • capabilities: openSession must be true for betting games; booleans for submitAction, forfeitExpiredSession, cancelStuckRandomness, resize.
  • Dynamic height: call observeGameContentSize(hostApi) from @chain-protocol/casino-sdk/guest after the bridge resolves so the host can size the iframe to your current content.

Example: examples/coinflip-public/game.manifest.json.

Game description: each locales[<locale>].description is the catalog’s game description for that locale. It is optional. When the platform builds your game, it reads the manifest from the build’s preview deployment and copies every non-empty description of 500 characters or fewer into the catalog; a longer one is dropped with a warning in the build log. A player sees the description of their locale, a locale of the same language (pt-BR for pt), or else the one of defaultLocale. A game with no description shows none. A frontend-only update brings in the new build’s descriptions.

Cover image: assets.coverUrl is the game’s catalog cover image. It is either a path relative to game.manifest.json, which points into the build’s preview deployment (for example cover.webp in your frontend’s public folder), or an absolute https:// URL. Any other scheme is skipped. The image must be PNG, JPEG or WebP, at most 2 MB, 2:3 portrait (width to height, within about 2%) and at least 230×345. When the platform builds your game, it copies the image into platform storage, so the catalog keeps working if your origin moves. While the manifest names a usable cover image, the submit form shows it read-only and takes no upload. An unreachable or unusable image is a warning in the build log that gives the reason, and the submit form then asks for an upload. A frontend-only update brings in the new build’s cover image, or keeps the current one when the new manifest has none.

gameId: Must match the canonical id derived from on-chain game registration (canonicalCasinoGameId in manifest.ts — strips trailing Game, lowercases, alphanumeric only). Host rejects mismatches.

Alignment with main web app (if integrated in-repo later)

Section titled “Alignment with main web app (if integrated in-repo later)”

Internal rules for the Next.js app:

  • User-visible strings go through i18n (packages/web/src/i18n/locales/en.json), not hardcoded English in JSX.
  • Follow existing shadcn / Tailwind patterns if contributing UI inside packages/web (not required for a static guest bundle).
  • Slot games often use heavy animation; prefer reduced motion via prefers-reduced-motion for accessibility.
  • Ensure tap targets and contrast work at the height your game reports to the host.

manifest.assets.iconUrl is optional; used in discovery when provided. manifest.assets.coverUrl is the catalog cover image; see “Cover image” above.