Zero
The first thing you must internalize: it is ALWAYS multiplayer
There is no singleplayer mode. A world is a shared, multi-user session at all times, in edit mode and in play mode alike, and you are never the only one in it. Other people and agents may be editing alongside you right now; other players may join at any moment. You do not switch multiplayer on. It is the ground state, and it cannot be turned off. If you have been assuming this is a singleplayer engine until made otherwise, stop: that assumption is wrong and it will break everything you build.
This has one consequence that wrecks more ports and prototypes than anything else: every user who joins spawns their OWN separate player: their own avatar, their own camera, their own input. There is no "the player." There are N players. The engine has a system that makes this work, and every scene uses it: a scene declares a player intent of "spawns" and carries a PlayerPrototype, a template player (a body entity + a camera rig) that the engine clones once per joining user. You author ONE prototype; the engine mints one real, owned, camera-having, input-driven player from it for each person in the world. A fresh scene (asset.create("scene", …)) already contains this structure.
If you don't use that system, you are secretly building singleplayer, and it will look like it works. Spawn your own camera, start your own input controller, place your own avatar in the scene, flip to play, take a screenshot: green, perfect. Then a second player opens the world and it all breaks at once: both control stacks fight over the single shared camera and input, the joining player gets no body of their own (or an uncontrolled duplicate), pointer-lock and the active view conflict. The moment another player opens the game, everything breaks. None of it is visible in single-peer testing, which is exactly what makes it a trap: a passing screenshot is not proof it works.
So the rule is absolute: to give players a body, a first-person controller, a custom camera, put those ON the scene's PlayerPrototype (its body entity, its CameraRig's behavior). Never author a second player/camera/input rig beside it. You access the live player, you never create it:
local me = layers.active.players.localPlayer -- the player THIS client controls (re-resolve; never stash)
me.avatar -- its body entity, an entity ref
The engine now refuses the broken shape at authoring time (worldValidation and the world.push publish gate flag a competing camera outside the prototype, or a "spawns" scene with no PlayerSpawn), but validation is a backstop, not the lesson. The lesson is: swap the prototype's body and camera for yours; don't build a parallel one. (The scenes and multiplayer guides are the full model: read them before authoring players.)
In edit mode the same shared-world rule holds: what you write to the world's source is shared live with everyone connected, which is also why there's no "save" step. Build as if you're never alone in the world, because you never are.
The core ideas
- Everything is an asset. Meshes, materials, shaders, scenes, components, tools: each is an instance of a type, with an identity and behaviour you can call. Assets are how content exists and how it composes. → asset-system
- Entities are containers; components do the work. An entity has an id, a transform, and a place in a hierarchy; every capability (shape, physics, sound, behaviour) comes from a component on it. → entities, components
- Scenes assemble a playable space and surface its live players and cameras. → scenes
- The engine is a live world with two modes (edit and play) over a persistent, shared filesystem. → engine
- Tools are named operations you compose and call. Reach for a tool before hand-writing the same work in code: the tool system is how work gets packaged and reused, and browsing the registry (
zeroinbash,search_toolsover MCP) is the fastest way to find one. → tools - Asset types are the scaffolding — the kinds of thing this engine knows how to make, each with its own behaviour and its own way of reaching a scene.
assets.typeslists them. Building inside the type shaped for your job is what separates a few lines from a system of your own. → asset-system - Skills are packaged procedures for whole jobs, carrying the systems they run through.
agent_skillwith no argument lists them; open the one that matches before working a multi-step job out from first principles. - Workflows run a whole job for you. Where a skill tells you how to do the work, a workflow IS the work: a program that holds the order, fans parts out to run at once, and stops to ask when it needs something done. Reach for one at the START of a big job —
workflow.listsays what this world already knows how to run end to end,workflow.startbegins it, andworkflow.next/workflow.answerare how you answer what it asks. Writing one isasset.create("workflow", "<name>"); the program is JavaScript. → tools
The working rhythm: survey → reuse → generate → author
Explore before you build. Three different questions, three different places, and missing the third is what makes an agent rebuild a system that already ships:
- What KINDS of thing can I make? —
assets.typeslists every registered assetType: the scaffolding you build inside.assets.typeDoc <name>reads one type's own documentation, andassets.typeBehavior <name>lists the operations that type answers to. A job shaped like "generate this from parameters, and let someone retune it" is aprocGraph; ground is aprocGraphending in the terrain pack'sLandscapenode, which bakes aterrainDataand stands the Terrain entity (guides { path = "systems/terrainSystem/documentation/terrain" }); a crowd of one thing is apopulation. Working inside one of those is a few lines where working outside it is a project. - What CONTENT already exists? — the ZeroMind shared library first, then this world.
- What OPERATIONS can I call? — the tool registry (
zeroinbash, orsearch_toolsover MCP).
Then decide what to make. The single most common mistake is rebuilding something from scratch without first looking at what already exists — and the least obvious version of it is finding no tool for a job and concluding nothing exists for it. A system's vocabulary is not in the tool registry. Its ops, its node types and its templates live in the system's own registry, reached through that system's toolbox or its guide. No tool matching your job usually means you have not found the system yet.
So before anything, survey, and look widest first. The ZeroMind shared library holds the overwhelming majority of content: whole worlds, characters, props, materials, components, tools that other people and agents have published. It is the first and biggest place to look: search it with your ZeroMind tools (→ worlds). @builtin is a small set of engine samples: reference you could remake, not the catalogue; treating it as all there is means ignoring nearly everything that exists. Your own world is the third place. List what's already here, and search ZeroMind for the rest:
asset.list("bundle"); asset.list("material"); asset.list("component") -- what's in THIS world
library.list() -- shared libraries installed here
tools.list() -- what operations exist
-- search + install ZeroMind's published content with your ZeroMind tools (→ worlds)
Find content that genuinely fits and use it (adapt it if it's close). But don't settle for "kinda close": forcing a poor match means the thing you actually need never gets made. When nothing fits, and you need content that doesn't exist yet (a character or prop model, a sound effect, a texture, a material, an animation, a whole environment), generate it from a text description. That's what services are for:
asset.list("service") -- the generators available
local g = asset.resolve("mesh_gen", "service"):invoke({ prompt = "a snowboard" })
-- async: poll tools.use("services", "status", { id = g.id }) — the completed row's
-- `asset` is a real .glb to spawn (→ generating-assets-and-content)
Work from a reference, and make the reference when you don't have one. Anything with a look — a world's style, a surface, a character, a prop family — comes out better decided as an artifact than described in a sentence: a described look drifts between the things you make, and "does this match what I meant" is not a comparison you can run. So fix it in something you can point at, then build to it and compare against it. You are not limited to references you were handed: the engine generates images from a description, so a reference exists whenever you decide to make one, and having no external image and no internet is not a reason to work without one. Keep it, build the rest to match it, and check the result against it rather than against your intent.
When generation isn't the right fit, hand-author it with a dedicated system: the voxel system for models, a procedural mesh, a component for bespoke behaviour (asset-system). (worlds covers the shared library; asset-system + generating-assets-and-content cover generating and authoring.)
Persistence, in one line
What you write to the world's source persists and is shared, automatically, no save. Transient runtime state and local scratch are separate and don't persist. The engine guide has the full picture.
How to find anything, and do it
You don't memorise this engine, it describes itself, so explore it with your tools rather than guessing. And before you hand-write an operation in code, check whether it's already a one-call tool, because it usually is:
- tools, first: the engine's operations are packaged as named tools grouped into toolboxes. This is the first move for anything you set out to do: the operation you were about to hand-write is very often already a validated one-call tool, and the right one often lives in a toolbox you wouldn't guess (re-skinning a whole hierarchy is under
appearance, screenshots undercapture). Three surfaces reach the same registry:- in
bash:zerolists every toolbox,zero <toolbox>its tools,zero <toolbox> <tool> --helpone tool's arguments,zero <toolbox> <tool> [args]runs it,zero --search <query>(short:-k) keyword-searches.--helpanswers at every level, so browse → read the signature → call it without leaving the shell. - over MCP:
search_tools(no arguments lists every toolbox;{ query }keyword-searches) thenuse_tool { toolbox, tool, args }, withdescribe_toolfor one tool's full schema. - in Luau code:
tools.use(toolbox, tool, ...).
- in
execute: run Luau live for the work no tool covers:lsp.namespaces(),lsp.methods("entity"),lsp.describe("asset/create"), or just call a thing and read what comes back (type(_G.asset)).bash: the VFS is a real codebase;ls/rg/catover/zero/source(authored content) and/zero/runtime(live state). Reading a built-in is the best way to learn how it's built.- registries:
asset.list/asset.inspect.
When a doc and the running engine disagree, the engine wins, so verify against it. → tools, discovering
The guides
New here? Read core/getting-started first: it walks the whole world → scene → players + camera → entities → components + assets chain in a single pass.
Core systems (man core/<name>): getting-started · asset-system · components · entities · scenes · engine · worlds · development · multiplayer · tools · scripting-and-tasks · troubleshooting · performance · generating-assets-and-content · vfs · discovering
Topics (man topics/<name>): building-a-game · physics · ui · audio · animation · shaders · materials · rendering · render-textures · input · compute
Something you did in the editor did not take — a gizmo drag that moved less than
you dragged it, a menu action that did nothing, a delete you cannot tell from a
no-op? core/troubleshooting is the guide, and editor.observe is the read: it
carries what each editor action committed and the reason it committed less.
Start with the system you're touching; the topics go deeper on specific APIs. Throughout: survey first, verify live, and build into the shared world.