Install

Pick the import style that fits your build.

The SDK ships in three formats from one build:

FormatBest forEntry inside dist/
ESMVite, Webpack, Rollup, esbuild, Bun, Denodist/index.js + dist/index.d.ts
CJSNode-style require, legacy bundlersdist/index.cjs + dist/index.d.cts
IIFEA single <script> tag — no build stepdist/gamee-sdk.iife.js

There are no peer dependencies.

This is the path most game projects use right now. You build the SDK once, then your game’s package.json points at the built folder.

a) Download the prebuilt SDK#

Grab the ready-to-link SDK folder from GitLab — no checkout, no build step:

gitlab.gamee.io/gamee/gamee-sdk/-/tree/master/build

The build/ folder is the same payload as packages/sdk/dist/, just surfaced at the top of the repo so you don’t have to dig through four nested folders to find the SDK output. Drop it somewhere your project can reference (e.g. a vendor/ directory). At the top level you’ll find:

build/
├── index.js          # ESM entry
├── index.cjs         # CJS entry
├── index.d.ts        # ESM type declarations
├── index.d.cts       # CJS type declarations
├── gamee-sdk.iife.js # IIFE bundle (window.Gamee)
└── *.map             # source maps

Pick one of the two patterns below.

Pattern A — file: dependency (simplest, copies on install).

// your-game/package.json
{
  "dependencies": {
    "@gamee/sdk": "file:../gamee-sdk/packages/sdk"
  }
}
npm install

Works with npm, pnpm, yarn. The whole packages/sdk/ folder (including dist/) is copied into your node_modules. Re-run npm install after every SDK rebuild to refresh.

Pattern B — copy build/ into your repo (no Node toolchain on the game side).

mkdir -p your-game/vendor/gamee-sdk
cp -R gamee-sdk/build/* your-game/vendor/gamee-sdk/
// your-game/package.json
{
  "dependencies": {
    "@gamee/sdk": "file:./vendor/gamee-sdk"
  }
}

Commit vendor/gamee-sdk/ so the build is reproducible without network access.

c) Use it#

After either pattern, the import is the same in every recipe and example on this site:

import { gamee } from '@gamee/sdk';

await gamee.init({ capabilities: ['saveState'] });

Need a fresh, isolated instance (tests, multiple games on one page)?

import { createSdk } from '@gamee/sdk';

const sdk = createSdk({ allowedOrigins: ['https://games.gameeapp.com'] });
await sdk.init({ capabilities: [] });

CommonJS works too:

const { gamee } = require('@gamee/sdk');
gamee.init({ capabilities: [] }).then(() => gamee.gameReady());

2 — Self-hosted IIFE script tag (window.Gamee)#

For prototypes, level editors, or games that have no build step. Grab gamee-sdk.iife.js from the repo’s build/ folder and serve it from your own static host. The IIFE bundle attaches a Gamee global to window:

<!doctype html>
<html>
  <head>
    <script src="/vendor/gamee-sdk/gamee-sdk.iife.js"></script>
  </head>
  <body>
    <canvas id="game"></canvas>
    <script>
      // `Gamee` is the same surface as the package.
      // Use `Gamee.gamee` (singleton) or `Gamee.createSdk(...)`.
      Gamee.gamee.init({ capabilities: [] }).then(() => {
        Gamee.gamee.gameReady();
      });
    </script>
  </body>
</html>

The global is Gamee (capital G). The lowercase Gamee.gamee is the default singleton; Gamee.createSdk and Gamee.GameeError live on the same namespace.

3 — npm registry (preview — not live yet)#

# Future. Does not work today.
npm install @gamee/sdk
# pnpm add @gamee/sdk
# yarn add @gamee/sdk

Once published, the same approach will also unlock the ESM-from-CDN form for no-build setups:

<script type="module">
  // Future. Does not work today.
  import { gamee } from 'https://esm.sh/@gamee/sdk';
</script>

TypeScript#

The SDK is written in TypeScript and ships its own .d.ts and .d.cts files. No separate @types/... install is needed — types resolve automatically as soon as your editor sees node_modules/@gamee/sdk/dist/index.d.ts (or the .d.cts for CJS projects).

The SDK targets ES2020 and ships ESM by default. The minimum settings:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    // "bundler" works for Vite/esbuild/Webpack/Rollup. Use "node16" if you
    // run TypeScript directly under Node.js without a bundler.
    "moduleResolution": "bundler",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
  },
}

If moduleResolution is set to the legacy "node", switch to "bundler" or "node16" — otherwise the type bundler may not pick up the package’s exports map and you’ll get spurious “cannot find module” errors.

Importing types#

Every public type is re-exported from the package root. Pull them in alongside the runtime imports:

import { gamee, createSdk, GameeError } from '@gamee/sdk';
import type {
  // Lifecycle
  SdkOptions,
  InitOptions,
  GameInitResponse,
  GameOverData,
  // Domain
  Capability,
  Platform,
  Environment,
  GameContext,
  MissionData,
  PurchaseDetails,
  PurchaseResult,
  RewardedLoadResult,
  RewardedResult,
  // Events
  GameeEvents,
  GameeEventName,
  // Errors
  GameeErrorCode,
  // Bridge (for tests / custom transports)
  PlatformBridge,
  BridgeListener,
} from '@gamee/sdk';

Typing your own handlers#

Event payloads are derived from the GameeEvents map, so handlers infer their argument shape automatically:

// `payload` is inferred as `{ gameSeed?: string; resetState?: boolean }`
gamee.on('start', (payload) => {
  if (payload.resetState) clearLocalProgress();
  startRun(payload.gameSeed);
});

// Unknown event names fail to compile:
gamee.on('not-a-real-event', () => {}); // ❌ TS2769

If you want to extract a payload type by name:

import type { GameeEvents } from '@gamee/sdk';

type StartPayload = GameeEvents['start'];
// → { gameSeed?: string; resetState?: boolean }

Narrowing the unknown event fields#

Two events carry unknown so the SDK doesn’t lock you into a platform shape:

  • avatarUpdate{ avatar: unknown }
  • miningEventUpdate{ miningEvent: unknown }

Cast in the handler with whatever type matches your platform’s contract:

interface AvatarPayload {
  url: string;
  rarity: 'common' | 'rare';
}

gamee.on('avatarUpdate', ({ avatar }) => {
  const a = avatar as AvatarPayload;
  ui.setAvatar(a.url);
});

Save state and init data are strings#

initResponse.saveState and initResponse.initData are typed as string | undefined — the platform hands you serialized JSON. Parse defensively:

interface Save {
  level: number;
  gold: number;
}

const ctx = await gamee.init({ capabilities: ['saveState'] });
const save: Save | null = ctx.saveState ? (JSON.parse(ctx.saveState) as Save) : null;

The corresponding write side accepts unknown and stringifies for you:

gamee.gameSaveState({ level: 4, gold: 200 } satisfies Save);

Typing errors#

GameeError has a typed code field. Exhaustive switch is the idiomatic recovery pattern:

import { GameeError, type GameeErrorCode } from '@gamee/sdk';

function handle(err: unknown): void {
  if (!(err instanceof GameeError)) throw err;
  const code: GameeErrorCode = err.code;
  switch (code) {
    case 'CAPABILITY_MISSING':
      /* … */ break;
    case 'BRIDGE_TIMEOUT':
      /* … */ break;
    case 'BRIDGE_ERROR':
      /* … */ break;
    case 'BRIDGE_OVERFLOW':
      /* … */ break;
    case 'VALIDATION':
      /* … */ break;
    case 'INVALID_STATE':
      /* … */ break;
    case 'NOT_SUPPORTED':
      /* … */ break;
  }
}

TypeScript with the IIFE script tag#

The IIFE bundle attaches window.Gamee at runtime. To get types in a TypeScript project that also uses the script-tag form, declare the global:

// src/types/gamee.d.ts
import type * as GameeNs from '@gamee/sdk';

declare global {
  interface Window {
    Gamee: typeof GameeNs;
  }
  const Gamee: typeof GameeNs;
}

export {};

You still need @gamee/sdk installed (option 1) for the type imports — but the runtime entirely comes from the <script> tag.

Verifying the install#

A 5-line health check you can paste into any new game:

import { gamee, SDK_VERSION, ALL_CAPABILITIES } from '@gamee/sdk';

console.log('SDK', SDK_VERSION, 'platform:', gamee.getPlatform());
console.log('declarable capabilities:', ALL_CAPABILITIES);

If getPlatform() returns 'web' and you are running outside the GAMEE platform, that’s expected — see the architecture page for what changes on iOS and Android.

Switch to a desktop

The GAMEE SDK emulator and docs are built for screens wider than 1280 px. Open this page on a laptop or desktop browser to get the full experience.

If you're already on a wide screen, drag your window wider and this banner will disappear.