---
title: "Generating assets & content"
description: "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…"
section: "Core"
slug: "core-generating-assets-and-content"
canonical: "https://origozero.ai/docs/core-generating-assets-and-content"
updated: "2026-09-06T20:54:34.686889268+00:00"
tags: ["documentation", "guide"]
---

# Generating assets & content

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" }
```

## Three surfaces, pick the one that matches where you are

Like every toolbox, the `services` tools answer on three surfaces, one per context (the tool system is covered in full in the tools guide):

**`zero`, from the shell.** `zero services` lists the verbs and `zero services <verb> --help` prints one verb's arguments and types, so you can read the call before you make it:

```bash
zero services list
zero services generate mesh_gen '{"prompt":"a wooden treasure chest"}'
```

**`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):

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

All three 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:

```lua
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`:

```lua
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.

## The two inputs every call takes

Alongside an operation's own inputs, the input table carries two fields the call itself takes. `list` publishes both on every operation, marked `framework = true`:

- **`operation`** — which of the service's operations to run, by name. Each entry's `default` names the operation a call runs when you leave this out; where it is absent the service refuses the call and names its operations, because picking one for you would spend credits on a guess, and `list` marks `operation` `required` there. Read the entry rather than assuming: a service can offer several operations and still run one of them unasked.
- **`asset_path`** — where the run writes what it generates: a full path carrying the operation's extension. Leave it out and the asset lands at `/zero/source/generated/<kind>/<the prompt, slugified>.<ext>`, which is fine for a probe and unhelpful for content you are filing into a project. An operation that produces a *set* of files (`pbr_gen`'s map set) authors its own folder, so it does not take `asset_path` and refuses one.

```lua
-- a non-default operation, and a name of your choosing for what comes back
tools.use("services", "generate", "audio_gen", {
    operation = "speech",
    text = "Welcome aboard.",
    asset_path = "/zero/source/game/audio/lines/open1.mp3",
})
```

`generate` also takes the operation as a third argument, which is the same thing said in the other place — `generate("audio_gen", { text = "…" }, "speech")`. Name it once; naming it twice differently is refused.

## 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.
