Skip to content

Game Embedding

This guide covers how to embed Slotty Labs games in your platform via iframes and communicate with them using the postMessage protocol.

iframe HTML

html
<div style="width: 100%; max-width: 1200px; aspect-ratio: 16/9;">
  <iframe
    id="slotty-game"
    src="https://games.slottylabs.com/launch/slotty-slots?token=eyJ...&lang=en&lobby=https://yourcasino.com/lobby&demo=false&channel=desktop"
    style="width: 100%; height: 100%; border: none;"
    allow="autoplay; fullscreen"
    sandbox="allow-scripts allow-same-origin allow-popups allow-forms"
    loading="lazy"
  ></iframe>
</div>

Launch URL Format

https://games.slottylabs.com/launch/{gameId}?token={launchToken}&lang=en&lobby={lobbyUrl}&demo=false&channel=desktop

Query Parameters

ParameterRequiredTypeDescription
tokenYes (unless session)stringSingle-use SSO launch token (30s TTL)
sessionNostringExisting session token for reconnection
langNostringLocale code (default: en)
lobbyNostringURL-encoded lobby redirect URL
demoNobooleanEnable demo mode (default: false)
channelNostringdesktop or mobile (auto-detected if omitted)

TIP

Either token or session must be provided. Use token for initial launches and session for reconnections after network drops.

Launch Config

When the game loads, the launch page injects a global configuration object:

typescript
interface LaunchConfig {
  gameId: string;
  apiBaseUrl: string;        // e.g. "https://api.slottylabs.com"
  wsBaseUrl: string;         // e.g. "wss://ws.slottylabs.com"
  launchToken: string;       // from URL query param
  locale: string;            // e.g. "en"
  lobbyUrl: string | null;   // redirect on exit
  demoMode: boolean;
  channel: 'desktop' | 'mobile';
  tenantBranding: {
    primaryColor: string;    // hex color
    logoUrl: string | null;
    casinoName: string;
  } | null;
}

// Access at runtime:
const config = window.__SLOTTY_CONFIG__;

Demo Mode

When demo=true is passed:

  • An orange banner is displayed at the top of the game: "DEMO MODE — Not real money"
  • No real SSO token is required
  • A configurable demo balance is provided (default: 10,000 credits)
  • All game mechanics work identically to real-money mode
  • Provably fair verification is still available
  • No webhooks are fired

CSP Headers

The launch page sets the following Content Security Policy headers:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'unsafe-inline';
  style-src 'self' 'unsafe-inline';
  connect-src 'self' https://api.slottylabs.com wss://ws.slottylabs.com;
  frame-ancestors https://*.yourcasino.com;
  img-src 'self' data: https:;
  media-src 'self' https:;

WARNING

You must request that your operator domain be added to the frame-ancestors directive. Contact office@slottylabs.com with your domain(s).

postMessage Protocol

Games communicate with the parent window using the postMessage API.

Envelope Format

All messages follow this structure:

typescript
interface SlottyMessage {
  source: 'slotty-game';
  type: string;
  payload: Record<string, unknown>;
  timestamp: number; // Unix ms
}

Game → Parent Messages

TypePayloadDescription
game:loaded{ gameId, version }Game assets have loaded
game:ready{ gameId }Game is ready for player interaction
game:roundStart{ roundId, betAmount, currency }A new round has started
game:roundEnd{ roundId, winAmount, currency, duration }A round has completed
game:balanceUpdate{ balance, currency }Player balance changed
game:exitRequest{}Player clicked the exit/lobby button
game:fullscreenRequest{ enabled }Player toggled fullscreen
game:soundToggle{ muted }Player toggled sound
game:realityCheck{ sessionDuration, totalWagered, netResult }Reality check interval reached
game:sessionExpired{ reason }Session has expired or timed out
game:error{ code, message }An error occurred in the game

Parent → Game Messages

TypePayloadDescription
host:mute{ muted }Mute or unmute game audio
host:pause{}Pause the game (between rounds)
host:resume{}Resume a paused game
host:close{}Close the game gracefully
host:resize{ width, height }Notify game of container resize
host:deposit{ amount, currency }Notify game of a deposit

Example Listener

typescript
window.addEventListener('message', (event) => {
  // Always validate the origin
  if (event.origin !== 'https://games.slottylabs.com') return;

  const message = event.data;
  if (message.source !== 'slotty-game') return;

  switch (message.type) {
    case 'game:loaded':
      console.log(`Game ${message.payload.gameId} loaded (v${message.payload.version})`);
      break;

    case 'game:roundEnd':
      console.log(`Round ${message.payload.roundId} ended. Won: ${message.payload.winAmount}`);
      break;

    case 'game:exitRequest':
      // Redirect to lobby
      window.location.href = '/lobby';
      break;

    case 'game:balanceUpdate':
      updateBalanceDisplay(message.payload.balance, message.payload.currency);
      break;

    case 'game:realityCheck':
      showRealityCheckDialog(message.payload);
      break;

    case 'game:sessionExpired':
      handleSessionExpired(message.payload.reason);
      break;
  }
});

Sending Messages to the Game

typescript
const gameIframe = document.getElementById('slotty-game') as HTMLIFrameElement;

// Mute the game
gameIframe.contentWindow?.postMessage(
  { source: 'slotty-host', type: 'host:mute', payload: { muted: true } },
  'https://games.slottylabs.com',
);

// Gracefully close the game
gameIframe.contentWindow?.postMessage(
  { source: 'slotty-host', type: 'host:close', payload: {} },
  'https://games.slottylabs.com',
);