Developers

Documentation

Game SDK

Integrate leaderboards, achievements, save games, inventory, tokens, levels, and user data into your game.

Quick Start

The SDK is automatically injected into every game's HTML when it loads on dmnshd.gg. No script tag needed — dmnshdGameSDK is available as a global.

Call init() once when your game loads. This establishes the connection with the platform.

await dmnshdGameSDK.init();

That's it. You can now use all SDK methods. Every method returns a Promise.

TypeScript Support

Install the npm package for type definitions. The runtime SDK is injected automatically — the package only provides types.

npm install @dmnshd/sdk

Add a triple-slash reference or include @dmnshd/sdk/global in your tsconfig to get the dmnshdGameSDK global typed:

/// <reference types="@dmnshd/sdk/global" />

await dmnshdGameSDK.init();
const user = await dmnshdGameSDK.user.get(); // typed as SDKUser | null

Or import types directly:

import type { SDKUser, LeaderboardEntry } from '@dmnshd/sdk';

Error Handling

All SDK methods return Promises. If a request fails (network error, auth required, invalid input), the Promise rejects with an Error. Requests time out after 10 seconds.

try {
  await dmnshdGameSDK.leaderboard.postScore('default', score);
} catch (err) {
  console.error('Failed to submit score:', err.message);
}

User

GETdmnshdGameSDK.user.get()

Returns the current user or null if not logged in.

const user = await dmnshdGameSDK.user.get();
// { id, username, displayName, avatarUrl, tokens } | null

The tokens field contains the player's current platform token balance. Use this to check if the player can afford an item before calling inventory.purchase().

Tokens

SYNCdmnshdGameSDK.getTokenImageURL()

Returns the absolute URL of the platform's token coin image. Use this to display the token icon in your game's UI (shop screens, price tags, balance displays).

const coinUrl = dmnshdGameSDK.getTokenImageURL();
// "https://cdn.dmnshd.gg/coin.webp"

const img = document.createElement('img');
img.src = coinUrl;
img.width = 24;
img.height = 24;

This is a synchronous method — no await needed. Works before init() is called.

Leaderboards

GETdmnshdGameSDK.leaderboard.get(name, options?)

Fetch entries from a leaderboard. If the leaderboard doesn't exist yet, it will be created automatically when the first score is submitted.

namestring — Leaderboard name (e.g. "default", "speedrun")
options.limitnumber — Max entries to return (default 50)
const entries = await dmnshdGameSDK.leaderboard.get('default', { limit: 10 });
// [{ rank, score, metadata, createdAt, user: { id, username, ... } }]
GETdmnshdGameSDK.leaderboard.getMy(name)

Get the current player's own entry on a leaderboard. Returns null if the player has no entry or is not logged in.

const myEntry = await dmnshdGameSDK.leaderboard.getMy('default');
// { rank, score, metadata, createdAt } | null
GETdmnshdGameSDK.leaderboard.getAroundMy(name, options?)

Get leaderboard entries around the current player's rank. Useful for showing "your neighborhood" on the leaderboard. Returns an empty list if the player has no entry.

namestring — Leaderboard name
options.limitnumber — Number of entries above and below the player (default 5)
const { entries, myRank } = await dmnshdGameSDK.leaderboard.getAroundMy('default', { limit: 3 });
// entries: [{ rank, score, user, ... }]
// myRank: 42 | null
POSTdmnshdGameSDK.leaderboard.postScore(name, score, metadata?)

Submit a score. For BEST leaderboards (default), only updates if the new score beats the player's existing entry. For SUM leaderboards, scores accumulate. The SDK caches the player's best score locally to skip redundant server calls. Leaderboards are auto-created on first submission.

namestring — Leaderboard name
scorenumber — The score value
metadataobject — Optional extra data (e.g. { level: 5 })
const result = await dmnshdGameSDK.leaderboard.postScore('default', 1500, { level: 5 });
// { success, updated, entry: { rank, score, metadata, createdAt } }

Leaderboards support two aggregation modes: BEST (default) keeps only the player's best score, SUM accumulates all submitted scores. Configure this in your game's dashboard.

Save Games

Save and load arbitrary JSON data in numbered slots (0–99). Each slot holds one save per player.

GETdmnshdGameSDK.saveGame.list()

List all save slots the player has used for this game.

const saves = await dmnshdGameSDK.saveGame.list();
// [{ id, slot, data, createdAt, updatedAt }]
GETdmnshdGameSDK.saveGame.load(slot)

Load data from a specific slot. Returns null if the slot is empty.

const data = await dmnshdGameSDK.saveGame.load(0);
// { level: 5, hp: 100, inventory: [...] } | null
POSTdmnshdGameSDK.saveGame.write(slot, data)

Write data to a slot. Creates or overwrites the existing save.

await dmnshdGameSDK.saveGame.write(0, {
  level: 5,
  hp: 100,
  inventory: ['sword', 'potion'],
});
DELETEdmnshdGameSDK.saveGame.remove(slot)

Delete a save slot.

await dmnshdGameSDK.saveGame.remove(0);

Achievements

Define achievements in your game's dashboard, then unlock them at runtime via the SDK. Achievements award points to the player's profile.

GETdmnshdGameSDK.achievement.getAll()

Get all non-secret achievements for this game, including which ones the current player has unlocked.

const { achievements, unlockedKeys } = await dmnshdGameSDK.achievement.getAll();
// achievements: [{ id, key, name, description, iconUrl, points }]
// unlockedKeys: ['first-kill', 'level-up']

Secret achievements won't appear in getAll() but can still be unlocked via unlock().

POSTdmnshdGameSDK.achievement.unlock(key)

Unlock an achievement by its key. Safe to call multiple times — returns the existing unlock if already unlocked. Shows an in-game toast notification on first unlock.

const result = await dmnshdGameSDK.achievement.unlock('first-kill');
// { achievement: { key, name, points, ... }, unlockedAt, alreadyUnlocked }

Inventory

Define items in your game's dashboard, then grant and consume them at runtime via the SDK. Supports stackable items (potions, ammo) and singular items (unique equipment).

GETdmnshdGameSDK.inventory.getCatalog()

Get all items defined for this game. Returns the full catalog regardless of what the player owns.

const items = await dmnshdGameSDK.inventory.getCatalog();
// [{ slug, name, description, thumbnailUrl, price, type, itemClass, metadata, ... }]

The price field is the token cost. Items with price: 0 are free to grant.

GETdmnshdGameSDK.inventory.get()

Get the current player's owned items for this game, including quantities.

const owned = await dmnshdGameSDK.inventory.get();
// [{ slug, name, type, ownedCount, acquiredAt, expiresAt, ... }]
POSTdmnshdGameSDK.inventory.purchase(itemSlug, count?)

Purchase an item for the player. If the item has a token price, the cost is deducted from the player's balance. For stackable items, increments the count. For singular items, only one can be owned.

itemSlugstring — The item's slug from your catalog
countnumber — Quantity to grant (default 1, stackable only)
const result = await dmnshdGameSDK.inventory.purchase('health-potion', 3);
// { success, tokens, item: { slug, name, type, ownedCount, acquiredAt } }
// tokens = updated balance after purchase

Throws an error if the player has insufficient tokens or already owns a singular item.

POSTdmnshdGameSDK.inventory.consume(itemSlug, count?)

Use or consume an owned item. Decrements the count for stackable items. Removes the item entirely if the count reaches zero.

itemSlugstring — The item's slug
countnumber — Quantity to consume (default 1)
const result = await dmnshdGameSDK.inventory.consume('health-potion');
// { success, remaining: 2 }

Levels

Let players create, share, and play each other's levels. Level data is arbitrary JSON (geometry, objects, scripts, etc.) up to 512 KB. Call recordPlay when a level session starts, and submitCompletion when the player finishes — this keeps the completion rate accurate.

GETdmnshdGameSDK.levels.list(options?)

Browse published levels. Returns metadata only — level data is not included in list results.

options.sort"newest" | "popular" | "featured" (default: newest)
options.difficulty"EASY" | "MEDIUM" | "HARD" | "EXPERT"
options.tagsstring[] — Filter by tags (all tags must match)
options.page / options.limitnumber — Pagination (default limit: 20, max: 50)
const result = await dmnshdGameSDK.levels.list({ sort: 'popular', limit: 10 });
// {
//   data: [{ id, slug, name, creatorName, difficulty, tags, likeCount, playCount,
//            completionRate, thumbnailUrl, publishedAt, ... }],
//   pagination: { page, limit, total, pages }
// }
GETdmnshdGameSDK.levels.listMy(options?)

List the current player's own levels across all statuses (DRAFT, PUBLISHED, ARCHIVED). Use this to let players manage their created levels.

options.page / options.limitnumber — Pagination (default limit: 20, max: 50)
const result = await dmnshdGameSDK.levels.listMy({ limit: 10 });
// {
//   data: [{ id, slug, name, status, difficulty, ... }],
//   pagination: { page, limit, total, pages }
// }
GETdmnshdGameSDK.levels.get(id)

Fetch a published level including its full data payload. Only works for published levels.

const level = await dmnshdGameSDK.levels.get(id);
// { ...metadata, data: { /* your level JSON */ } }
GETdmnshdGameSDK.levels.getMy(id)

Fetch one of the current player's own levels, including drafts. Useful for previewing a level before publishing.

const draft = await dmnshdGameSDK.levels.getMy(id);
// Returns level at any status (DRAFT, PUBLISHED, ARCHIVED)
POSTdmnshdGameSDK.levels.create(input)

Create a new draft level. The level is private until publish() is called. Level data must not exceed 512 KB.

gameIdstring — ID of the game this level belongs to
namestring — Level name (max 100 chars)
dataobject — Level content JSON (max 512 KB)
difficultyoptional — "EASY" | "MEDIUM" | "HARD" | "EXPERT" (default: MEDIUM)
tagsstring[] — Up to 10 tags for discoverability
remixedFromIdoptional — ID of a published level this is based on
const level = await dmnshdGameSDK.levels.create({
  gameId: 'abc123',
  name: 'The Floating Ruins',
  data: { tiles: [...], spawnPoint: { x: 0, y: 5, z: 0 } },
  difficulty: 'HARD',
  tags: ['platformer', 'ruins'],
});
// { id, slug, status: 'DRAFT', ... }
PATCHdmnshdGameSDK.levels.update(id, input)

Update a level's fields. Passing a new data value increments the level's version. Owner only.

await dmnshdGameSDK.levels.update(id, {
  name: 'The Floating Ruins v2',
  data: { tiles: [...], spawnPoint: { x: 0, y: 6, z: 0 } },
  tags: ['platformer', 'ruins', 'hard'],
});
POSTdmnshdGameSDK.levels.uploadThumbnail(levelId, image)

Upload a preview image for a level you own (while the game is embedded on dmnshd.gg). Uses a Blob or File — e.g. from canvas.toBlob(), fetch(...).then(r => r.blob()), or a file input. Max 256 KB; JPEG, PNG, or WebP. Returns the updated level including thumbnailUrl.

const level = await dmnshdGameSDK.levels.uploadThumbnail(levelId, imageBlob);
// level.thumbnailUrl → '/uploads/levels/…' (use with your CDN / site origin)
POSTdmnshdGameSDK.levels.publish(id)

Make a draft or archived level publicly visible. Owner only.

const level = await dmnshdGameSDK.levels.publish(id);
// { ...level, status: 'PUBLISHED', publishedAt: '...' }
DELETEdmnshdGameSDK.levels.remove(id)

Soft-delete a level. It becomes invisible to other players but is not permanently removed. Owner only.

await dmnshdGameSDK.levels.remove(id);
POSTdmnshdGameSDK.levels.like(id)

Toggle a like on a published level. Calling again removes the like.

const { liked, likeCount } = await dmnshdGameSDK.levels.like(id);
// liked: true | false
// likeCount: updated total
POSTdmnshdGameSDK.levels.recordPlay(id)

Record that a player started playing a level. Call this when the level loads. This increments playCount and is used to calculate the completion rate — so always call it before submitCompletion.

// Call when the level starts
await dmnshdGameSDK.levels.recordPlay(levelId);
POSTdmnshdGameSDK.levels.submitCompletion(id, input)

Record a completed run. Increments completionCount and updates the completion rate. All fields are optional.

scoreoptional number — Run score
durationMsoptional number — Time to complete in milliseconds
metadataoptional object — Any extra game-specific data
const completion = await dmnshdGameSDK.levels.submitCompletion(levelId, {
  score: 4200,
  durationMs: 93_500,
  metadata: { deaths: 3, coinsCollected: 12 },
});
// { id, levelId, userId, score, durationMs, metadata, createdAt }

Full Example

A minimal game integration. The SDK script is injected automatically — just use the global.

<!DOCTYPE html>
<html>
<head>
  <title>My Game</title>
</head>
<body>
  <script>
    async function main() {
      // Initialize SDK
      await dmnshdGameSDK.init();

      // Check if player is logged in
      const user = await dmnshdGameSDK.user.get();
      if (user) {
        console.log('Welcome back,', user.displayName || user.username);
        console.log('Token balance:', user.tokens);
      }

      // Load saved progress
      const save = await dmnshdGameSDK.saveGame.load(0);
      let level = save ? save.level : 1;

      // ... game logic ...

      // Save progress
      await dmnshdGameSDK.saveGame.write(0, { level: level });

      // Submit score
      await dmnshdGameSDK.leaderboard.postScore('default', 1500);

      // Check your rank and nearby players
      const { entries, myRank } = await dmnshdGameSDK.leaderboard.getAroundMy('default', { limit: 3 });
      console.log('My rank:', myRank);

      // Unlock achievement
      if (level >= 10) {
        await dmnshdGameSDK.achievement.unlock('veteran');
      }

      // Browse the item catalog and check prices
      const catalog = await dmnshdGameSDK.inventory.getCatalog();
      const potion = catalog.find(i => i.slug === 'health-potion');

      // Purchase an item (deducts tokens)
      if (potion && user && user.tokens >= potion.price) {
        const result = await dmnshdGameSDK.inventory.purchase('health-potion', 2);
        console.log('Remaining tokens:', result.tokens);
      }

      // Load and play a UGC level
      const levels = await dmnshdGameSDK.levels.list({ sort: 'popular', limit: 5 });
      const picked = levels.data[0];
      if (picked) {
        const levelData = await dmnshdGameSDK.levels.get(picked.id);
        await dmnshdGameSDK.levels.recordPlay(picked.id);
        // ... load levelData.data into your game engine ...

        // When player finishes:
        await dmnshdGameSDK.levels.submitCompletion(picked.id, {
          score: 3800,
          durationMs: 120_000,
        });
      }
    }

    main();
  </script>
</body>
</html>

Notes

01

The SDK is injected into every .html file served from your game's upload directory. It uses postMessage to communicate with the platform — no direct network requests.

02

The player must be logged in on dmnshd.gg for write operations (scores, saves, achievements, level creation) to work. Read operations like user.get() return null for guests.

03

Leaderboards are created automatically when you submit the first score to a new name. To configure sort order (ascending or descending), create the leaderboard from your game's dashboard first.

04

Achievements must be defined in your game's dashboard before they can be unlocked via the SDK. Secret achievements won't appear in getAll() but can still be unlocked.

05

Save slots are numbered 0–99. Each player gets their own save data per slot, per game.

06

Inventory items must be defined in your game's dashboard before they can be purchased via the SDK. Items with a token price deduct from the player's balance. Stackable items can be accumulated; singular items can only be owned once.

07

Leaderboards support two aggregation modes: BEST (default) keeps only the player's best score, SUM accumulates all submitted scores. Configure this in your game's dashboard.

08

Tokens are the platform's currency. Players buy tokens on dmnshd.gg and spend them on in-game items. Set item prices (in tokens) from your game's dashboard. Items with price 0 are free to grant.

09

Level data is capped at 512 KB per level. The version field increments each time data is updated — use it to cache-bust loaded level content.

10

Always call recordPlay when a level session begins and submitCompletion only when the player actually finishes. This keeps the completion rate meaningful.