Poplay SDK Integration Guide
This guide explains the minimum integration required before submitting an HTML5, Canvas, or WebGL game to Poplay.
1. Package Structure
Upload a ZIP package that can run as a static web game.
index.html
assets/
game.js
poplay.config.json
Requirements:
- Put
index.htmlin the package root. - Use relative asset paths.
- Make sure the game can run inside an iframe.
- Do not hardcode leaderboard, cloud save, or realtime service URLs.
- Put
poplay.config.jsonnext toindex.html.
2. Add the SDK
Add the SDK script in index.html.
<script src="https://poplay.io/sdk/poplay-sdk.js"></script>
Initialize Poplay before calling platform APIs.
await Poplay.init();
3. Player
const player = await Poplay.player.get();
console.log(player.id);
console.log(player.displayName);
console.log(player.locale);
Notes:
player.idis a stable anonymous identity persisted by Poplay for the current browser.- Reopening the same game keeps the same
player.idunless browser storage is cleared. sessionTokencan rotate between sessions without changing cloud save ownership.
4. Leaderboard
Declare a leaderboard in poplay.config.json.
{
"capabilities": {
"leaderboard": true
},
"leaderboards": [
{
"id": "global",
"name": "Global",
"sort": "desc",
"keepBest": true
}
]
}
Submit a score after a round ends.
await Poplay.leaderboard.submit("global", {
score: 12800,
metadata: {
level: 7,
duration: 93.2
}
});
Read leaderboard entries.
const entries = await Poplay.leaderboard.list("global", {
limit: 20
});
Read the current player's entry.
const me = await Poplay.leaderboard.me("global");
Rules:
- Submit scores only after meaningful gameplay events.
- Do not submit every frame or every second.
scoremust be a number.- Keep
metadatasmall.
5. Cloud Save
Declare cloud save in poplay.config.json.
{
"capabilities": {
"cloudSave": true
},
"save": {
"slots": ["default", "save/main", "save/profile"],
"maxBytes": 65536
}
}
Use Poplay.save.* for a single primary save slot.
Use Poplay.json.* when the game wants multiple JSON documents such as:
save/mainsave/profilesave/config
Every JSON document key must be declared in save.slots.
Use Poplay.publicJson.* for game-scoped public JSON documents that are not bound to individual players.
Load save data.
const save = await Poplay.save.get("default");
if (save) {
restoreProgress(save);
}
Read save metadata.
const meta = await Poplay.save.getMeta("default");
if (meta) {
console.log(meta.version, meta.updatedAt);
}
Write save data.
const result = await Poplay.save.set(
"default",
{
level: 12,
exp: 3400,
skins: ["red", "gold"],
settings: {
sound: true
}
},
{
expectedVersion: meta?.version
}
});
console.log(result.version, result.updatedAt);
For a first write or overwrite flow, omit expectedVersion.
await Poplay.save.set("default", {
level: 12,
exp: 3400,
skins: ["red", "gold"],
settings: {
sound: true
}
});
Delete save data.
await Poplay.save.delete("default");
Read a JSON document.
const profile = await Poplay.json.load("save/profile");
Read JSON document metadata.
const profileMeta = await Poplay.json.getMeta("save/profile");
Write a JSON document.
await Poplay.json.save(
"save/profile",
{
nickname: "player_name",
favoriteSnake: "green"
},
{
expectedVersion: profileMeta?.version
}
);
Delete a JSON document.
await Poplay.json.delete("save/profile");
Equivalent top-level helpers are also available.
const saveDoc = await Poplay.loadJsonDocument("save/main");
const meta = await Poplay.getJsonDocumentMeta("save/main");
await Poplay.saveJsonDocument("save/main", saveDoc, {
expectedVersion: meta?.version
});
await Poplay.deleteJsonDocument("save/main");
Read public JSON.
const liveConfig = await Poplay.publicJson.load("config/live");
Read public JSON metadata.
const liveMeta = await Poplay.publicJson.getMeta("config/live");
Write public JSON.
await Poplay.publicJson.save(
"config/live",
{
season: 2,
eventName: "spring"
},
{
expectedVersion: liveMeta?.version
}
);
Delete public JSON.
await Poplay.publicJson.delete("config/live");
Equivalent top-level helpers are also available.
const publicDoc = await Poplay.loadPublicJsonDocument("config/live");
const publicMeta = await Poplay.getPublicJsonDocumentMeta("config/live");
await Poplay.savePublicJsonDocument("config/live", publicDoc, {
expectedVersion: publicMeta?.version
});
await Poplay.deletePublicJsonDocument("config/live");
Rules:
- Save data must be JSON.
- Default save limit is 64KB.
- Do not store images, audio, or large level bundles in cloud save.
- JSON document keys must be declared in
save.slots. - Include a small
schemaVersionfield in your save object. - Use
expectedVersionwhen you want conflict protection across tabs or devices. - Handle HTTP 409 by reloading the latest save before retrying.
- Public JSON writes currently trust any valid game session for the same game. Add developer-only authorization before using this in production.
6. Realtime Rooms
Declare realtime rooms in poplay.config.json.
{
"capabilities": {
"realtime": true
},
"realtime": {
"maxPlayers": 2,
"visibility": "public",
"hostStrategy": "close-on-host-leave"
}
}
Quick match.
const room = await Poplay.rooms.quickMatch({
maxPlayers: 2
});
Create a public room.
const room = await Poplay.rooms.create({
maxPlayers: 2,
visibility: "public"
});
Join a room.
const rooms = await Poplay.rooms.list();
const room = await Poplay.rooms.join(rooms[0].id);
Send realtime messages.
room.broadcast("player-state", {
x: player.x,
y: player.y,
direction: player.direction
});
room.on("message", (message) => {
if (message.event === "player-state") {
applyRemotePlayerState(message.playerId, message.payload);
}
});
Important:
- Poplay provides room creation, joining, presence, and message transport.
- Your game still needs to implement gameplay synchronization.
- Your game decides player state, world state, host authority, disconnect handling, and match settlement.
- Keep realtime payloads small, ideally below 4KB.
7. Complete Config Example
{
"capabilities": {
"leaderboard": true,
"cloudSave": true,
"realtime": true
},
"leaderboards": [
{
"id": "global",
"name": "Global",
"sort": "desc",
"keepBest": true
}
],
"save": {
"slots": ["default"],
"maxBytes": 65536
},
"realtime": {
"maxPlayers": 2,
"visibility": "public",
"hostStrategy": "close-on-host-leave"
}
}
8. Submission Checklist
Before submitting:
index.htmlexists at the package root.- The SDK script is included.
Poplay.init()is called before platform APIs.poplay.config.jsonmatches the platform features used by the game.- Leaderboard, save, and realtime calls use
PoplayAPIs. - The game runs inside an iframe.
- Asset paths are relative.
- The game does not contain third-party platform branding or external game portal links.