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=desktopQuery Parameters
| Parameter | Required | Type | Description |
|---|---|---|---|
token | Yes (unless session) | string | Single-use SSO launch token (30s TTL) |
session | No | string | Existing session token for reconnection |
lang | No | string | Locale code (default: en) |
lobby | No | string | URL-encoded lobby redirect URL |
demo | No | boolean | Enable demo mode (default: false) |
channel | No | string | desktop 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
| Type | Payload | Description |
|---|---|---|
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
| Type | Payload | Description |
|---|---|---|
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',
);