Guides

Gamification: XP & achievements

AdminUpdated Sep 19, 2026

Gamification: XP & achievements

Signed-in players earn XP (which levels them up platform-wide) and collect achievements. Your game decides when to grant; the platform decides what actually lands — every request is capped, gated on a valid play session, and audited at upload, so no game can inflate the economy.

1. Declare achievements in game.json (max 30). The XP each is worth is set by its tier (common/uncommon/rare/epic/legendary) — you can't set XP directly:

{
  "title": "My Game",
  "category": "arcade",
  "gamification": {
    "achievements": [
      { "key": "first_win", "name": "First Win", "description": "Win a round", "tier": "common", "icon": "🏆" },
      { "key": "no_damage", "name": "Untouchable", "description": "Win without taking damage", "tier": "rare", "icon": "🛡️" }
    ],
    "xp": { "note": "how your game grants XP", "leaderboard": true }
  }
}

2. Grant from your game via the SDK, on genuine gameplay (a win, a score, a milestone) — never on load, in a loop, or on a timer:

GameSDK.awardXp(20, "win").then(function (r) {
  // r = { granted, total, level, leveledUp } — granted may be < 20 (caps) or 0 (guest)
});
GameSDK.unlockAchievement("first_win");   // must be a declared key; idempotent
GameSDK.submitScore(score);               // leaderboard (per-game high scores)
GameSDK.getPlayer().then(function (p) { /* { signedIn, level, handle } */ });
GameSDK.on("levelUp", function (e) { /* e.level */ });

The portal shows XP/level-up/achievement toasts for you. Guests are prompted to sign in; their calls are safe no-ops.

Saving progress, stats & high scores (spec §5.7)

Persist anything — save states, settings, and stats — under named slots. It auto-routes to the player's account (cloud, synced across devices) when they're signed in, and to browser localStorage when they're a guest. Your code is identical either way; each slot holds up to 64 KB.

// Save state / progress / stats. Store JSON for structured stats.
GameSDK.saveData("progress", JSON.stringify({ level: 7, coins: 340 }));
GameSDK.saveData("stats", JSON.stringify({ kills: 1200, distanceM: 5300, plays: 88 }));

GameSDK.loadData("progress").then(function (v) {
  var s = v ? JSON.parse(v) : { level: 1, coins: 0 };  // null if no save yet
});

GameSDK.listSaves().then(function (slots) { /* [{ key, updatedAt }] — e.g. save-slot UI */ });
GameSDK.deleteSave("progress");

// High scores go to the per-game leaderboard:
GameSDK.submitScore(score);
// …and read the player's own standing for a "Your best: X (#Y)" HUD:
GameSDK.getMyScore().then(function (r) { /* { score, rank } — nulls for a guest */ });

So a game can offer save slots, cross-device cloud progress, persistent stats, and its own leaderboard/rank HUD — all through the SDK, with zero backend of your own. And when a guest signs in, their localStorage saves are automatically migrated into their account (existing cloud slots are never overwritten), so a player never loses the progress they made before signing up.

Social — friends & profiles in-game (spec §14)

Build immersive social features right into your game: an in-game leaderboard, "add friend" prompts, and links to players' profiles. The portal owns auth and UI; you just call these with handles you get from getLeaderboard() / getFriends().

// This game's high-score leaderboard. scope: "global" (default) or "friends".
GameSDK.social.getLeaderboard("global").then(function (rows) {
  // rows = [{ rank, handle, displayName, level, score }, …]
  // Render your own in-game board, with an "add friend" button per row:
  rows.forEach(function (r) {
    // GameSDK.social.addFriend(r.handle) / GameSDK.social.viewProfile(r.handle)
  });
});

// Send a friend request as the signed-in player. Resolves { handle, status }
// where status is "outgoing" | "friends" (auto-accepted a reciprocal request)
// | "self" | "error". Rate-limited; guests are nudged to sign in.
GameSDK.social.addFriend("someHandle").then(function (r) { /* r.status */ });

// The signed-in player's friends — e.g. to show which pals have a high score.
GameSDK.social.getFriends().then(function (friends) {
  // friends = [{ handle, displayName, avatarUrl, level }, …]
});

// Open a player's public profile (portal opens it in a new tab).
GameSDK.social.viewProfile("someHandle");

// getPlayer() now also returns the player's own handle (null for guests).
GameSDK.getPlayer().then(function (p) { /* p.handle */ });

// Fires whenever a friend request goes out from your game:
GameSDK.on("friendAdded", function (e) { /* { handle, status } */ });

All handles come from getLeaderboard() or getFriends() — the portal validates them, enforces the friend-request rate limit, and shows a confirmation toast, so a game can never silently friend-spam on a player's behalf.

Anti-abuse (automatic). At upload an economy audit inspects your code for farmable patterns (grants on load / in loops / timers, undeclared achievement keys, absurd amounts). Flagged games go to human review; egregious ones are rejected. At runtime XP is capped per-award, per-session, per-game-per-day, and per-user-per-day with diminishing returns, and only counts inside a valid play session (≥30s, real engagement). Award freely on real play — the caps handle the rest.


Was this page helpful?