create
`asset.create` — the single, generic "instance a new asset of an existing type" API. The same entry point scripts and agents use; there is deliberately no tool wrapper, because agents author in Luau and call `asset.create(...)` directly.
asset_create
asset.create — the single, generic "instance a new asset of an existing type"
API. The same entry point scripts and agents use; there is deliberately no tool
wrapper, because agents author in Luau and call asset.create(...) directly.
local inst = asset.create("material", "my_metal", { base_color = { 0.8, 0.7, 0.2 } })
-- → AssetRef: inst.path == "/zero/source/my_metal.material", inst.guid, plus the
-- material type's ref methods.
asset.create makes a new instance of an already-registered type by running
that type's onCreate(name, opts) behaviour hook (declared in its
behavior.luau) and writing the produced files to
/zero/source/<name>.<typeName>/ — the one authored location, in every mode.
While play runs, the play-mode write lock takes that write onto the play
shadow: live in the session, disk source untouched, listed by
vfs.playShadowPaths(), and promoted or discarded on a guarded play-exit. The
asset created in play therefore carries the same identity, path and require
spelling it has in edit, and the session decides whether it stays.
A create whose output the world reproduces on every load — one made from a
component or a scene entrypoint — writes to the copy-on-write runtime store at
/zero/runtime/assets/<identity>.<typeName>/ instead, so the code that rebuilds
it each load is its only source and the saved manifest never carries a second
copy.
Placement
Four opts keys are consumed by the framework before the type's onCreate
hook runs, and steer where the instance lands:
| Key | Effect |
|---|---|
folder | A relative subfolder under the source root: /zero/source/<folder>/<name>.<typeName>/. Groups a generator's output instead of accumulating it at the source root. |
into | A resolved container ref (.toolbox / .package) to author INSIDE — lands at <container>/<name>.<typeName>/ and registers as a member. Edit-mode only. |
dest | An absolute destination path, owned by the caller (an importer building a <name>.bundle/). |
overwrite | Re-author an existing destination in place, keeping its .meta guid so every reference stays valid. |
The name is always the asset's bare identity — the path goes in folder:
local rock = asset.create("mesh", "rock", { positions = p, indices = i, folder = "terrain/props" })
-- → rock.path == "/zero/source/terrain/props/rock.mesh"
dest and into both take precedence over folder. folder decides the
asset's identity, so it applies in every context: the runtime store is flat and
spells that identity in one folder name.
-- the same call from a component's awake()
-- → rock.path == "/zero/runtime/assets/terrain.props.rock.mesh"
-- → rock.identity == "terrain.props.rock", as it is from an execute
Types without an onCreate hook fall back to cloning their verbatim
template/ skeleton to the same destination, so create works for every type.
Returns the created asset's AssetRef (.path / .guid plus the type's ref
methods — the same interned instance asset.resolve returns) on success and
raises (via error) on bad arguments or a failing onCreate.
Installed onto the FFI asset namespace by the prelude (M.installInto(asset)).
See docs/specs/runtime-asset-copying.md.
Scoped to this part · feeds back into the world's score.