Tooling
Metaloot SDK
@metaloot/sdkis a zero-dependency ESM package for game code: stream hosted assets by id, read the player's auth session, and join multiplayer rooms. It works in the browser, Node 20+, and Workers, with optional adapters for three.js and Babylon.js.
Installation#
npm install @metaloot/sdkThe package is tree-shakeable and split into entry points so your bundle only carries what you use:
| @metaloot/sdk | The unified client (createMetaloot()) plus re-exports of the most common helpers. |
| @metaloot/sdk/assets | Asset URLs, metadata, manifests, and loaders. |
| @metaloot/sdk/auth | Player session for games using Metaloot auth. |
| @metaloot/sdk/multiplayer | joinRoom() — same API as the zero-install /__metaloot/multiplayer.js module. |
| @metaloot/sdk/three | three.js adapter (peer dep three >= 0.160). |
| @metaloot/sdk/babylon | Babylon.js adapter (peer deps @babylonjs/core, @babylonjs/loaders >= 7). |
The Unified Client#
import { createMetaloot } from "@metaloot/sdk";
const ml = createMetaloot(); // { auth, assets, multiplayer }
const session = await ml.auth.getSession();
const url = await ml.assets.loadAssetObjectUrl("treasure-chest-1a2b3c4d");
const room = await ml.multiplayer.joinRoom("lobby");createMetaloot(options?) accepts { studioOrigin?, token?, authBasePath?, fetch? } — all optional. A ready-made default instance is exported as metaloot. Every method is also available as a named export from its module if you prefer plain functions.
Assets#
Load hosted assets by id or slug — no bundling, no CORS setup. The SDK picks the same-origin edge proxy on Metaloot hosting and the Studio API everywhere else:
import {
assetUrl, loadAsset, loadAssetObjectUrl,
getAsset, getAssetManifest, listAssets, loadAssetFile,
loadAnimation, loadAnimationObjectUrl,
} from "@metaloot/sdk/assets";
// A loader-ready URL (blob:) for any GLTF loader:
const url = await loadAssetObjectUrl("treasure-chest-1a2b3c4d");
// Raw bytes for an engine's parse API:
const buffer = await loadAsset("treasure-chest-1a2b3c4d");
// Metadata, search, and pack files:
const asset = await getAsset("frost-witch-c78f6ed7");
const found = await listAssets({ query: "sword", kind: "model3d" });
const manifest = await getAssetManifest("kenney-interface-sounds");
const bytes = await loadAssetFile("kenney-interface-sounds", { path: manifest.files[0].path });- Variants —
assetUrl(id, { variant: "auto" | "source" | "lod" });auto(default) serves the game-ready LOD once built. - Private assets — pass
{ token: "mtl_api_…" }(needs theassets:readscope); dev-only, never ship tokens in a bundle. - Animations —
loadAnimationObjectUrl(id, "idle")per rigged preset; all presets share one skeleton.
Full asset docs, including the CLI side of the pipeline: Assets.
Auth#
For games using Metaloot auth (deployed on Metaloot hosting, or running the @metaloot/auth adapter):
import { getSession, signIn, signOut } from "@metaloot/sdk/auth";
const session = await getSession();
// { signedIn: true, user: { id, name, imageUrl, … }, scope, expiresAt }
// or { signedIn: false }
if (!session.signedIn) signIn(); // redirects to Sign in with MetalootThe default base path is /auth/metaloot — override with getSession({ basePath }) if you mounted the adapter elsewhere. On Metaloot hosting, window.metaloot.session gives you the same data with no import at all.
Multiplayer#
import { joinRoom, MetalootAuthRequiredError } from "@metaloot/sdk/multiplayer";
const room = await joinRoom("lobby");
room.on("message", ({ from, data }) => handleInput(from, data));
room.send({ kind: "move", x: 3, y: 7 });Identical API to the zero-install /__metaloot/multiplayer.js module — use the npm import when you want types and bundling, the platform module when you want zero dependencies. Rooms require the game to be on Metaloot hosting. Full event and method reference: Multiplayer.
Engine Adapters#
The adapters take an asset from URL to a placed, animated, cleaned up object in your scene — rendering and gameplay stay yours:
// three.js
import { loadThreeAsset } from "@metaloot/sdk/three";
const hero = await loadThreeAsset("ember-mage", {
scene, // added to your scene, grounded and scaled
animations: "available", // load all rigged presets into one mixer
targetHeight: 1.8,
shadows: true,
autoPlay: "idle",
});
hero.update(clock.getDelta()); // tick the mixer each frame
hero.play("run"); // crossfade between presets
hero.dispose(); // full cleanup
// Babylon.js
import { loadBabylonAsset } from "@metaloot/sdk/babylon";
const chest = await loadBabylonAsset("treasure-chest-1a2b3c4d", { scene });@metaloot/sdk or @metaloot/sdk/assets never pulls in three.js or Babylon.