Embed SDK
@carverjs/embed-sdk lets your game talk to the CarverJS marketplace shell from inside the play page. It is dependency-free, around 1 KB, and works with any engine — CarverJS, Phaser, Three.js, plain canvas, or a static HTML build.
Games on the marketplace run inside a sandboxed <iframe> on their own origin (https://g-{gameId}.carverjs.dev). The shell — the page a player opens to play your game — listens for a small set of typed postMessage signals. This SDK sends them for you, so you never hand-roll postMessage shapes, and it stays in lockstep with the shell's validator.
import { carver } from "@carverjs/embed-sdk";
carver.progress(40); // loading bar in the shell (0–100)
carver.ready(); // hides the loader — call on first frame
carver.score(1280, "points"); // feeds player-profile stats
carver.event("level-complete", { level: 3 });CarverJS is under active development. The SDK is versioned independently of the engine; APIs may change between minor versions until 1.0.
Install #
npm install @carverjs/embed-sdkThe package ships ESM and CommonJS builds with TypeScript types. It has no dependencies and no peer dependencies — drop it into any front-end build.
Quick start #
Wire the two calls every game should make — progress while loading, ready on the first rendered frame — then report whatever stats fit your game.
import { carver } from "@carverjs/embed-sdk";
async function boot() {
carver.progress(0);
await loadAssets((pct) => carver.progress(pct)); // 0–100 as you go
startRenderLoop();
carver.ready(); // first frame is up — shell hides its loader
}
// during play
function onCoinCollected(total: number) {
carver.score(total, "coins");
carver.event("coin-collected", { total });
}
// when the run ends
function onGameOver(finalScore: number) {
carver.score(finalScore, "points");
carver.exit();
}
boot();Every call is a safe no-op outside an iframe — for example when you open your build locally during development — and in non-browser environments (SSR, tests). Nothing in the SDK ever throws, so you can leave the calls in place everywhere.
How it works #
Your game and the shell live on different origins, so they can only talk through window.postMessage. The SDK wraps that channel:
Outbound (game → shell):
ready,progress,score,event,error,requestFullscreen, andexitpost a typed message to the parent window. The shell validates every message — exact origin match, strict shape check — and silently drops anything malformed.Inbound (shell → game):
getIdentity()andsubscribe()receive messages, but only from the direct parent window.
┌─────────────────────────┐ postMessage ┌──────────────────────┐
│ your game │ ───────────────► │ marketplace shell │
│ g-{gameId}.carverjs.dev │ ◄─────────────── │ carverjs.dev │
└─────────────────────────┘ (validated) └──────────────────────┘You never need to know the shell's origin, build a message object by hand, or check whether you're embedded — the SDK handles all of it.
The boot lifecycle #
The shell shows a loader until your game signals it is ready. Drive it with two calls:
| Call | When |
|---|---|
carver.progress(percent) | As assets load. Clamped to 0–100. Drives the loader's progress readout. |
carver.ready() | The moment your first frame renders. The shell hides the loader and starts counting the play. |
If your game never calls ready(), the shell falls back to a short grace window after the iframe loads and then reveals the game anyway — so a plain HTML game without the SDK still works. Calling ready() is strongly recommended: it makes the loader accurate and the play-count honest.
When something goes fatally wrong, call carver.error(code, message) — the shell swaps the game for an error card instead of leaving the player staring at a spinner.
What's next #
API Reference — every method, with signatures and when to call it.
Player Identity — prove which signed-in player is playing, to your own backend.
Security & Origins — the trust model, and how to verify identity tokens.