Contents
Install The JavaScript SDK And Save Your First Game Data
Install @persistlyapp/sdk, configure PersistlyGameSaves, load local data, save one default game save, and sync safely.
This is the smallest useful JavaScript integration: configure the SDK, load one default game save, save local data, and sync it when the game reaches a safe moment.
Use this when your game is a browser game, Vite app, Phaser game, PlayCanvas game, Cocos Creator game, or another JavaScript-based wrapper.
Small idle, incremental, and casual games can start with saveData and loadData. If the game later needs manual saves, campaigns, or multiple slots, switch those calls to saveSlot(slotId) and loadSlot(slotId).
Install The SDK
Install the public package from npm:
npm install @persistlyapp/sdkImport the high-level game-save facade:
import { PersistlyGameSaveStatus, PersistlyGameSaves } from "@persistlyapp/sdk";Use PersistlyGameSaves for normal game code. The lower-level PersistlyClient exists for advanced wrappers and direct runtime API work.
Configure The SDK
Configure once when your game boots:
await PersistlyGameSaves.configure({
runtimeKey: "ps_test_replace_me",
});What this does:
runtimeKeyselects your Persistly project and environment.ps_test_keys should be used during development and QA.- In browsers, the SDK uses Persistly's built-in local storage adapter by default so it can keep local drafts, account session data, and slot mapping.
Load Before Starting The Game
Read local game data before you start gameplay:
const loaded = await PersistlyGameSaves.shared.loadData();
if (loaded.status === PersistlyGameSaveStatus.LocalFound && loaded.data) {
startGameFromState(loaded.data);
} else {
startNewGame();
}This is intentionally local-first. The game can start quickly even if the player is offline.
Make The First Save
Save a small JSON-compatible data object:
await PersistlyGameSaves.shared.saveData({
level: 1,
coins: 50,
checkpoint: "forest-gate",
});saveData writes local gameplay data immediately. It uses the default autosave slot under the hood and does not require a network request to protect current progress.
Add SlotInfo For Menus
Use slotInfo for slot-select screens, save-slot lists, and support context:
await PersistlyGameSaves.shared.saveData(
{
level: 1,
coins: 50,
checkpoint: "forest-gate",
},
{
slotInfo: {
label: "Autosave",
chapter: "Forest",
lastPlayedAt: new Date().toISOString(),
},
},
);Keep real gameplay data in the data object. Keep display/search fields in slotInfo.
Sync Safely
For development, call forceSyncData when you want to prove the cloud path works:
const result = await PersistlyGameSaves.shared.forceSyncData();
if (result.status === PersistlyGameSaveStatus.Synced) {
console.log("Synced to Persistly.");
}
if (result.status === PersistlyGameSaveStatus.Conflict) {
showConflictRecovery();
}For normal gameplay, call syncDue from safe lifecycle moments:
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "hidden") {
void PersistlyGameSaves.shared.syncDue();
}
});
window.addEventListener("online", () => {
void PersistlyGameSaves.shared.syncDue();
});syncDue respects the runtime sync policy returned by the API. With the default policy, remote sync is throttled by a minimum interval instead of sending a cloud request for every local save. forceSyncData is for explicit one-save moments such as manual save, checkpoint, pause, or release testing.
Update The Same Save
Call saveData again:
async function onCheckpointReached(gameState: GameState) {
await PersistlyGameSaves.shared.saveData({
level: gameState.level,
coins: gameState.coins,
checkpoint: gameState.checkpointId,
updatedAt: new Date().toISOString(),
});
void PersistlyGameSaves.shared.syncDue();
}Same default save, same logical player progress. New data replaces the local draft for the default save.
One-Save Game
Many idle, incremental, and small single-player games only need one slot:
await PersistlyGameSaves.shared.saveData(gameState);
const loaded = await PersistlyGameSaves.shared.loadData();Use this for games where the player always continues the same run.
Multiple Slots Or Manual Slots
If your game has multiple slots or manual save slots, use stable slot keys:
await PersistlyGameSaves.shared.saveSlot("warrior", warriorState, {
slotInfo: { characterName: "Borin", className: "Warrior", level: 12 },
});
await PersistlyGameSaves.shared.saveSlot("mage", mageState, {
slotInfo: { characterName: "Ayla", className: "Mage", level: 9 },
});
const slots = await PersistlyGameSaves.shared.listSlots();Persistly stores each named slot under the player account. Your game can use listSlots to build a slot-select or load-game screen.
Account-First Games
Games with account login, shared inventory, premium balances, or cross-device restore can use account helpers in addition to game data and slots. That path still uses the same facade, but it adds explicit account/session handling when your backend needs to restore a player on another browser or device.
What To Test Next
- Save while offline, reload the page, and confirm
loadDatarestores local data. - Go online and call
forceSyncData. - Save the same data twice and confirm the latest local data loads.
- Add a second slot and confirm
listSlotscan show both. - Add conflict handling before shipping cross-device resume.