The Pixlland SDK is a thin layer over postMessage that negotiates a session with the portal and exposes a typed Promise API. Every method is safe to call in standalone (no portal) — it degrades to local-only stubs so dev work never requires a server.
Include the SDK
Games loaded via the portal get the SDK for free from pixlland.com/sdk/v1/pixlland.js. For local dev or third-party embedding:
<script src="https://pixlland.com/sdk/v1/pixlland.js" defer></script>The script auto-exposes window.PixllandSDK. Bundled workflows can instead use the npm package:
import { PixllandSDK } from '@pixlland/sdk';Lifecycle, end to end
Every hosted game should follow this order. Deviating is not fatal, but it makes your game worse at the behaviours the portal tracks.
await init()— handshake with the portal. Returns{ gameId, locale, signedIn }. 4-second standalone fallback when no portal is detected so local dev always resolves.gameLoadingProgress(r)— drive the progress bar.ris clamped to[0, 1].gameLoadingFinished()— signal ready-to-play. Call exactly once.gameplayStart()— user clicked Play / round started. Gates ad breaks (only allowed during an active gameplay session).gameplayStop()— death, pause, main menu, or tab close. Pair with a previousgameplayStart.
Ad breaks
// Interstitial between rounds. Resolves when the ad finishes
// or instantly when no fill / no consent / standalone.
if (roundsBeaten % 3 === 0) await PixllandSDK.commercialBreak();
// Rewarded — ALWAYS check granted before unlocking the reward.
const { granted, reason } = await PixllandSDK.rewardedBreak();
if (granted) player.coins += 100;Both calls respect the player's cookie consent. If ads consent is revoked, commercialBreak() resolves immediately and rewardedBreak() returns { granted: false, reason: 'ad_blocked' }.
Accounts + cloud save
// Profile fields only — never email / tokens
const user = await PixllandSDK.account.get();
if (!user) await PixllandSDK.account.signIn();
// Short-lived (60 s) JWT to prove identity to YOUR backend
const token = await PixllandSDK.account.getToken();
fetch('/leaderboard/submit', {
headers: { Authorization: 'Bearer ' + token },
});
// Cross-device save — JSON serialisable, 64 KB max per key
await PixllandSDK.data.set('progress', { level: 7, coins: 42 });
const progress = await PixllandSDK.data.get('progress');data.* rows are scoped per-game + per-user — another author cannot read your saves.
Deep links
// Seeds with the current URL params from the portal page.
const url = PixllandSDK.shareableURL({ level: '5' });
// => https://pixlland.com/play/<slug>?level=5
// On cold load, read params the portal forwarded into the iframe.
const level = PixllandSDK.getURLParam('level'); // "5" | nullObservability
try { saveProgress(); }
catch (e) { PixllandSDK.captureError(e); } // pipes to Sentry
PixllandSDK.measure('progress', 'level', 'completed');
PixllandSDK.happyTime(1); // seconds of fun delivered this frameFull reference
Auto-generated TypeDoc site: /sdk-docs/. Source of truth is the TypeScript surface shipped with @pixlland/sdk.
