Log inGet started

Generating assets & content

You're building something — a scene, a game, a level — and you need an asset that doesn't exist yet: a 3D model of a dragon, a door-slam sound, a stone-tile texture, a walk animation, a whole…

Generation is driven through the services toolbox: discover what you can make, start a run, track it to a finished asset. You never resolve or invoke a generator by hand — you work the tool rail, and every generator behaves the same way regardless of what it produces.

What you can generate

Each generator is a service the toolbox lists for you, self-describing down to each operation's inputs and credit cost:

use_tool { toolbox = "services", tool = "list" }
-- mesh_gen   → a textured 3D mesh (GLB), from a prompt or a reference image
-- image_gen  → an image (PNG) from a prompt
-- pbr_gen    → a PBR material (albedo / normal / roughness / …) from a prompt
-- audio_gen  → music, a sound effect, or speech from a prompt
-- anim_gen   → a humanoid animation clip from a text prompt
-- world_gen  → a navigable gaussian-splat world from a prompt

Each service exposes one or more operations, and list reports every operation's inputs and credit cost — so you read what to pass without looking anywhere else.

The set is open — read list rather than memorising it, and check balance for the credits a run draws from:

use_tool { toolbox = "services", tool = "balance" }

Two surfaces — pick the one that matches where you are

Like every toolbox, the services tools are invoked two ways, one per context (the tool system is covered in full in the tools guide):

use_tool — the MCP call, when you're driving the engine from outside. search_tools finds a tool, use_tool runs it. Pass the toolbox, the tool, and positional args:

use_tool { toolbox = "services", tool = "generate",
           args = ["mesh_gen", { prompt = "a wooden treasure chest" }] }
-- → { id = "gen_0", service = "mesh_gen", operation = "generate" }

tools.use(toolbox, tool, ...) — from Luau, when you're authoring in code. Inside an execute() call it runs the tool and hands back the value directly (raising on failure):

local g = tools.use("services", "generate", "mesh_gen", { prompt = "a wooden treasure chest" })
-- g is the handle { id, service, operation }

Both do the same thing; the examples below use the Luau form for brevity.

Kick off a generation, then track it

generate returns immediately with a handle; the run takes minutes and outlives the call. Poll status across turns — each call is quick — until status == "completed", then the row's asset is the spawnable asset:

local g = tools.use("services", "generate", "mesh_gen", { prompt = "a wooden treasure chest" })

-- in a LATER turn rather than busy-polling this one:
local s = tools.use("services", "status", g.id)   -- { status, progress, asset, error, … }
if s.status == "completed" then
    -- s.asset is the imported, spawnable asset
end

The run imports its raw output into a spawnable asset — a mesh becomes a .bundle (mesh + material + texture), an image a .texture, a sound a .audio. s.asset points at that imported asset, ready to spawn / apply / play. A run that can't proceed fails with a legible reason on s.error ("out of credits", "not signed in", "upstream 401") rather than finishing empty. jobs lists every run this session; outputs lists what you've generated across sessions.

Dropping it into a scene you've already laid out

You don't have to stare at an empty slot while a generation runs, and you never have to leave a rough stand-in in the finished scene. Lay the scene out, then replace each entity with its generated asset in place — same position, rotation, scale, name, and parent — with appearance.replaceWithContent:

local g = tools.use("services", "generate", "mesh_gen", { prompt = "a carved oak chest" })
-- …poll services status to completion…
local asset = tools.use("services", "status", g.id).asset
tools.use("appearance", "replaceWithContent", "chest_slot", asset)

The entity named chest_slot (and its subtree) is removed and the generated model takes its exact place. This is how a scene blocked out with stand-ins becomes the finished thing — you swap each placeholder for the real, generated (or library) asset rather than shipping the stand-in.

Inputs beyond text

Most operations take a prompt, but inputs are whatever the operation declares — numbers (width, height), booleans (seamless), a public asset URL, or a local asset (e.g. feed an image to mesh_gen's from_image operation). The list tool shows each operation's exact inputs, so read it rather than guessing; select a non-default operation with operation:

tools.use("services", "generate", "mesh_gen",
    { operation = "from_image", image = "/zero/source/generated/images/ref.png" })

Credits

Generating consumes credits from your ZeroMind account — when a bot generates, from the human who owns the bot. Metering is server-side: a service names a provider offering and a logical endpoint; ZeroMind resolves the real upstream, injects the provider's secret, debits the price, and relays the result. The offering identity and the secret never appear in the service or your world. Read a run's cost up front in the list output, and check balance before a batch.

The throughline

Reach for the ZeroMind library and asset.list to see what already exists before building anything by hand; when the asset you need isn't there, generate it from a description through the services toolbox, then swap it into place. Authoring your own generator — becoming a provider others spend credits on — is the .service asset type; its README (types/service) is the worked reference.

  • documentation
  • guide