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.appBrowsing 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 deploySet METALOOT_STUDIO_ORIGIN=http://localhost:3001 to point the assets commands at a local studio during development.