Metaloot Studio Assets

Metaloot Studio turns an image or a text prompt into a game-ready 3D model (GLB), rigs it with animations, and hosts curated 2D, audio, and media packs. There are three ways to work with it, all sharing the same Metaloot login: studio.metaloot.app in the browser (explore, generate, manage your workspace), the metaloot CLI from the terminal (generate, rig, download — perfect for agents), and the @metaloot/sdk npm package in game code (stream hosted assets by id — no files bundled with your game).

Generating assets with the CLI

Install the CLI (npm install -g @metaloot/cli or npx @metaloot/cli) and sign in once with metaloot login — the same login used by metaloot deploy. Generation is asynchronous: a job is queued and typically finishes in 1–3 minutes. Pass --wait to block until it's done, or poll with metaloot assets status <id>.

# Text-to-3D
metaloot assets generate --prompt "low-poly treasure chest, stylized, game-ready" \
  --name "Treasure Chest" --wait

# Image-to-3D (local file or http(s) URL; --prompt optionally guides it)
metaloot assets generate --image concept.png --name "Ember Mage" --wait

# Check on a generation
metaloot assets status <asset-id>   # add --wait to block until done
  • --quality draft|standard|hd — default hd: best geometry and detailed PBR textures. draft is fastest and cheapest for iteration.
  • --visibility public|private — default private. Public assets appear on the Studio Explore page and games can hot-link them from their hosted URL. Flip later with metaloot assets publish <id> / unpublish <id>.
  • --category — a label for organizing assets: Characters, Creatures, Weapons, Props, Environment (default Characters).
  • --face-limit <n> — optional polygon budget. Omit to let the provider pick optimal topology; the studio then automatically builds a game-ready LOD (~15k faces) that hosted URLs serve once ready.
  • --json — machine-readable output on generate, list, explore, files, status, rig. The JSON object is printed last on stdout, after the human-readable lines.

Image tips: a single full-body subject in a neutral pose on a clean background, with every part physically connected (floating pieces and glow/particle effects get dropped during 3D reconstruction), matte lighting, nothing cropped. Keep local files under ~1 MB (JPEG works well); pass larger images by URL. Prefer a visual workflow? The Studio Create page runs the same pipeline in the browser.

Rigging & animations

A finished model can be rigged (auto-skeleton + skinning) with preset animation clips retargeted onto it. Each finished preset is hosted as its own GLB containing the rigged model plus that clip, and all presets share one skeleton — so a game can load them into a single animation mixer and crossfade.

metaloot assets rig <asset-id> --presets idle,walk,run --wait

# The asset JSON gains rigStatus and animations:
#   { "animations": { "idle": { "status": "success", "url": "…" }, … } }
# Each preset is hosted at /api/assets/<id>/animation/<preset>

Re-running rig retries failed presets and can add new ones; the rig itself is built only once per asset. Loading the clips in a game is covered in the SDK section below.

Consuming assets with @metaloot/sdk

Public assets are hosted at stable URLs with CORS enabled, so your game streams them by id or slug — no download step, nothing bundled with the game. The zero-dependency @metaloot/sdk package picks the right URL automatically and works in the browser, Node 20+, and Workers with any engine:

npm install @metaloot/sdk

import { loadAssetObjectUrl } from "@metaloot/sdk";
// or: import { … } from "@metaloot/sdk/assets"

// Streams the hosted GLB — works with three.js, Babylon, any GLTF loader:
const url = await loadAssetObjectUrl("treasure-chest-1a2b3c4d");
new GLTFLoader().load(url, (gltf) => scene.add(gltf.scene));
  • URL resolution — on a <name>.metaloot.app game origin, assetUrl() returns the same-origin, edge-cached proxy /__metaloot/assets/<id-or-slug>.glb (zero CORS concerns). Everywhere else it returns https://studio.metaloot.app/api/assets/<id-or-slug>/file, served with Access-Control-Allow-Origin: * for public assets. Both URLs also work directly with GLTFLoader.load(...)if you'd rather skip the SDK.
  • Variants — every 3D asset can have a full-resolution source file and a game-ready lod (~15k faces). The default variant: "auto"serves the LOD once it's ready and falls back to the source until then. Force one with assetUrl(id, { variant: "source" }) — responses carry an X-Metaloot-Variant header telling you which file auto resolved to.
  • Loading helpers loadAsset() returns an ArrayBufferfor any engine's parse API; loadAssetObjectUrl() returns a loader-ready blob: URL (revoke it when done).
  • Private assets — only served to their owner. Pass a CLI token as { token: "mlt_…" } in server-side code only — never ship a token in a game bundle. For browser games, make the asset public or download it and ship the file (below).

Rigged assets expose their animation presets through the same module — one GLB per preset, all sharing a skeleton:

import { getAsset, loadAnimationObjectUrl } from "@metaloot/sdk/assets";

const asset = await getAsset("frost-witch-c78f6ed7");
if (asset.animations?.idle?.status === "success") {
  const idle = await gltfLoader.loadAsync(await loadAnimationObjectUrl(asset.id, "idle"));
  const walk = await gltfLoader.loadAsync(await loadAnimationObjectUrl(asset.id, "walk"));
  const mixer = new THREE.AnimationMixer(idle.scene);
  mixer.clipAction(idle.animations[0]).play(); // crossfade to walk.animations[0] when moving
}

Alternatively — and always, for private assets — download the file and deploy it like any other static asset:

# Saved as <slug>.glb; for Vite, public/ is copied into dist/ by the build
metaloot assets download <asset-id> --dir public/assets
metaloot assets download <asset-id> --variant lod   # game-ready file (source is the default)

# Load it with your 3D stack, e.g. three.js:
#   new GLTFLoader().load("/assets/treasure-chest.glb", (gltf) => scene.add(gltf.scene));

metaloot deploy   # live at https://<name>.metaloot.app

Browsing the catalog: packs, files & metadata

Beyond generated 3D models, the studio hosts curated packs of sprites, textures, audio, and animations. Every listed file is served through the same Metaloot API with a hashed manifest, so game code never depends on a creator site. Discover assets from the CLI or the SDK:

# CLI
metaloot assets list        # your assets            [--category] [--kind] [--json]
metaloot assets explore     # public gallery — kinds: model3d|image|video|audio|sprite|texture|animation
metaloot assets files <pack-id>   # every hosted file in a pack: path, MIME type, size, SHA-256
// SDK
import { listAssets, getAsset, getAssetManifest, loadAssetFile } from "@metaloot/sdk/assets";

const swords = await listAssets({ query: "sword" });              // public gallery
const chars  = await listAssets({ category: "Characters", kind: "model3d" });
const asset  = await getAsset("treasure-chest-1a2b3c4d");
// { id, name, slug, status, progress, visibility, category, tags,
//   modelUrl, previewUrl, sourceModelUrl, lodModelUrl, lodStatus,
//   rigStatus, animations, createdAt, … }

// Pull one file out of a pack (omit path for the whole hosted ZIP):
const manifest = await getAssetManifest("kenney-interface-sounds");
const sound = manifest.files.find((f) => f.contentType === "audio/ogg");
const bytes = await loadAssetFile("kenney-interface-sounds", { path: sound.path });

Agent Instructions

Paste this into your coding agent to generate 3D assets and ship them with a game deployed via metaloot deploy. The whole flow — auth, generation, download, deploy — runs from the terminal with no dashboard step; the agent only needs a CLI token in METALOOT_TOKEN.

Generate game-ready 3D assets for this game with the Metaloot CLI and ship them with the deployed game. Everything runs from the terminal — no dashboard or browser step is needed.

Prerequisites:
- The Metaloot CLI: npx @metaloot/cli or npm install -g @metaloot/cli
- A CLI token (ask the owner for one from https://metaloot.app/cli/auth if it is not already available). Export it as METALOOT_TOKEN — it takes precedence over stored credentials and works for every command:
  export METALOOT_TOKEN="mlt_…"
  metaloot whoami   # verify auth before doing anything else

Generating assets (asynchronous — always pass --wait):
- Text-to-3D:
  metaloot assets generate --prompt "low-poly treasure chest, stylized, game-ready" --name "Treasure Chest" --wait
- Image-to-3D (local file or http(s) URL):
  metaloot assets generate --image concept.png --name "Ember Mage" --wait
- Optional flags: --visibility public|private (default private), --category Characters|Creatures|Weapons|Props|Environment (default Characters), --quality draft|standard|hd (default hd — best geometry + detailed PBR textures), --face-limit <n> (optional polygon budget; omit to let the provider pick optimal topology).
- Image tips for best 3D results: single full-body subject, neutral pose, clean background, all parts physically connected (floating pieces and glow/particle effects get dropped), matte lighting, nothing cropped. Local files should be under ~1 MB (JPEG works well); pass larger images by URL.
- Add --json for machine-readable output. The JSON object is printed last on stdout after human-readable lines, so strip everything before the first "{" when piping:
  metaloot assets generate --prompt "…" --name "…" --wait --json | sed -n '/^{/,$p' > asset.json
  The asset JSON has id, slug, status, progress, and (when finished) modelUrl/modelFormat.
- Other subcommands: metaloot assets list [--json] (your assets), metaloot assets explore [--json] (public gallery, --kind model3d|image|video|audio|sprite|texture|animation), metaloot assets status <id> [--wait] [--json], metaloot assets files <pack-id> [--json] (every hosted file in a curated pack: path, MIME type, size), metaloot assets publish <id> / unpublish <id> (flip visibility later), metaloot assets update <id> (name/description/category/visibility).

Animations (optional, for characters/creatures):
- Rig a finished model and retarget preset clips onto it:
  metaloot assets rig <asset-id> --presets idle,walk,run --wait
- Each finished preset is a hosted GLB (rigged model + that clip) at /api/assets/<id>/animation/<preset>; all presets share one skeleton, so load them into a single AnimationMixer and crossfade. In game code:
  import { getAsset, loadAnimationObjectUrl } from "@metaloot/sdk/assets";
  const asset = await getAsset("<asset-id>");        // has rigStatus + animations: { preset: { status, url } }
  const idleUrl = await loadAnimationObjectUrl(asset.id, "idle");

Getting the asset into the game — two paths:

A) Hosted asset (preferred when the asset is PUBLIC, i.e. generated with --visibility public): reference the hosted GLB by URL; no file ships with the game.
   - On a game deployed to <name>.metaloot.app, use the same-origin, edge-cached proxy (zero CORS concerns):
     /__metaloot/assets/<asset-id-or-slug>.glb
   - From any other origin, use the studio URL (public assets are served with Access-Control-Allow-Origin: *):
     https://studio.metaloot.app/api/assets/<asset-id-or-slug>/file
   - Or install @metaloot/sdk, which picks the right URL automatically and works with any engine:
     import { loadAssetObjectUrl } from "@metaloot/sdk";
     new GLTFLoader().load(await loadAssetObjectUrl("<asset-id>"), (gltf) => scene.add(gltf.scene));
   - Hosted URLs serve a game-ready LOD (~15k faces) automatically once the studio builds it, falling back to the full-res source until then. The SDK also exposes listAssets({ query, category, kind }) and getAsset(id) for discovering existing public assets before generating new ones.

B) Download and ship (required for PRIVATE assets — they are only served to their owner and are never hot-linkable):
   1. Download the GLB into a folder that ships with the deployed site:
      metaloot assets download <asset-id> --dir public/assets
      The file is saved as <slug>.glb (slug comes from the asset JSON). For Vite games use public/ so the build copies it into dist/; for static games deployed with --dir ., any non-dot folder works.
   2. Load the GLB with the game's existing 3D stack, e.g. three.js:
      new GLTFLoader().load("/assets/<slug>.glb", (gltf) => scene.add(gltf.scene));

Deploying:
- metaloot deploy builds the game and publishes it at https://<name>.metaloot.app (auth and multiplayer are provisioned automatically). METALOOT_TOKEN authenticates this too.
- Every CLI command exits non-zero on failure (including --wait when a generation fails), so plain && chaining is safe.

Verify end-to-end: after deploy, fetch the asset URL the game uses — https://<name>.metaloot.app/__metaloot/assets/<asset-id>.glb for hosted assets, or https://<name>.metaloot.app/assets/<slug>.glb for downloaded ones — confirm it returns the model (Content-Type: model/gltf-binary), then confirm the game renders it.

Docs: https://metaloot.app/docs/assets and the CLI README.

Fully scriptable

The whole pipeline runs non-interactively. Create a CLI token once at metaloot.app/cli/auth and export it as METALOOT_TOKEN — it authenticates every command, including metaloot deploy. Commands exit non-zero on failure (including --wait when a generation fails), so they chain safely:

export METALOOT_TOKEN="mlt_…"

metaloot assets generate --prompt "low-poly treasure chest" \
  --name "Treasure Chest" --visibility public --wait --json | sed -n '/^{/,$p' > /tmp/asset.json

ASSET_ID=$(node -p "JSON.parse(require('fs').readFileSync('/tmp/asset.json','utf8')).asset.id")

# Hot-link it from game code via @metaloot/sdk (public assets), or ship the file:
metaloot assets download "$ASSET_ID" --dir public/assets && metaloot deploy

Set METALOOT_STUDIO_ORIGIN=http://localhost:3001 to point the assets commands at a local studio during development.