---
title: "Bundle (asset type)"
description: "A bundle is a composable, spawnable entity assembly— a saved entity hierarchy plus its components, sub-assets (meshes, textures, animations, materials, sub-bundles), and any nested data, packaged…"
section: "Types"
slug: "types-bundle"
canonical: "https://origozero.ai/docs/types-bundle"
updated: "2026-07-31T07:12:26.497549300+00:00"
tags: ["asset-type", "reference"]
---

# Bundle (asset type)

There are **two ways** to bring a bundle into a scene — and the
difference matters enormously for scene saving and live editing. See
"How to spawn" below before you write anything.

If you want one snapshot of a whole world / level, use a `.scene`.

## Where it lives

- Source: `/zero/source/.../<Name>.bundle/`
- Identity: `<Name>` (the `.bundle` suffix strips for use in
  identities, but the on-disk folder keeps the suffix).
- Folder shape (container type — open child set):
  - `entity_template` — JSON of the entity hierarchy + components.
    **Required.**
  - `preview.png` — a rendered still of the assembled hierarchy, the
    bundle's persisted visual description. **Required.** Regenerated on
    every `entity_template` write; it syncs, publishes, feeds
    image-based search, and gives browsers a thumbnail.
  - Anything else: meshes / textures / materials / animations /
    sub-bundles. Validator allows arbitrary additional files
    (`allow_unlisted: true`).

## How to create one

Two paths, depending on whether you have an in-scene hierarchy or are
authoring from scratch:

```luau
-- From an existing in-scene entity: composes the given entity tree into a bundle.

local bundle = asset.create("bundle", "<Name>",<entityProxy/id>)


-- Or scaffold an empty bundle, then update it from an entity:
local bundleRef = asset.create("bundle", "<Name>")
bundleRef:update(entityId)
```

## How to spawn

### 1. `Asset` component — live-linked (the canonical way)

**This is the primary way to put a bundle into a scene that survives
saving as an asset reference.** Spawn an empty entity, attach the
`Asset` component, point it at the bundle:

```luau
local car = entity.spawn("car_1")
car.component.add("Asset", { source = "<bundleIdentity>" })
```

What you get:

- The bundle's hierarchy spawns as **temporary** children under the
  owning entity. They're real, addressable runtime entities, but
  they are NOT serialized into `scene.json` on save, only the Asset component and its entity is saved.
- **Live editing of the bundle is supported.** When you edit
  anything inside the `.bundle/` (entity_template, meshes,
  materials, sub-bundles, components), every entity in the world
  carrying that `Asset` component automatically tears down its
  spawned children and rebuilds them from the new template — same
  frame, no restart, no manual reload. The `onAssetReload` hook in
  `Asset.component/init.luau` drives this.
- **Per-instance overrides survive across reloads.** Move a spawned
  child, rename it, add/remove components on it, edit a component's
  public state — the changes land in the Asset's `public.diff` (a
  sparse delta keyed by the child's `original_id`) at save time and
  are replayed by `bundleRef:instantiate()` on next load. The bundle stays
  a single source of truth; per-spot tweaks ride on top.
- **Stable child ids across reloads.** `public.idMap` records the
  `original_id → runtimeId` assignment on first spawn; on every
  subsequent spawn the same mapping is reused so cross-entity
  references (e.g. `SkinnedModel.skeletonRoot = "ent_xxx"`) stay
  valid.

### 2. `bundleRef:instantiate` — baked, no live link

Resolve the bundle and instantiate it onto an entity — this spawns the
children **without** the live-link plumbing the `Asset` component
provides:

```luau
local car = entity.spawn("car_1")
asset.resolve("<Name>", "bundle"):instantiate(car)
```

What you get — and the trade-offs:

- The children spawn as **regular** (non-temporary) entities. On the
  next `layers.active:save()`, the entire spawned hierarchy is serialized into
  `scene.json` verbatim.
- **No live link to the bundle.** Editing the `.bundle/` after the
  fact has no effect on already-spawned hierarchies. The bundle was
  effectively consumed at spawn time.
- Scene-save / restore round-trip works the same as for any other
  hand-authored entity tree.

Use this path when you want a one-shot expansion of the bundle into
permanent scene state (e.g. you're populating a scene's static
layout from a parametric bundle and don't want a live link).

You can convert a live-linked instance into a baked one explicitly
via `Asset:bakeIntoScene()` — this drops the `Asset` component and
promotes the temporary children to permanent scene entities.

## How it operates

1. **Registration.** Writing into a `.bundle/` folder indexes the
   asset with the container-category restore plumbing.
2. **Inner refs.** Component fields that reference assets inside the
   bundle use the `__ref` shape (`{ "__ref": "<guid>", "name": "...",
   "type": "..." }`). The asset registry resolves them at spawn
   time.
3. **Identity preservation.** The `.bundle` suffix stays in the
   identity for container types — `<Name>.bundle` resolves under
   that exact string, distinguishing it from any `<Name>.scene` at
   the same stem.
4. **Hot reload.** Saving `entity_template` (or any file in the
   bundle directory) fires the engine's per-asset content-changed
   hook, which calls `onAssetReload("source")` on every entity
   whose `Asset` component points at the bundle. Each carrier
   despawns its children and re-instantiates against the new
   template — typically next frame.

## Discovery

- `asset.list("bundle")` — every registered bundle.
- `asset.inspect("<name>")` — sub-asset catalog, source, this type
  README.
- `cat /zero/source/<Name>.bundle` — same summary.

## Authoring conventions

- Prefer `bundle:update()` or `asset.create("bundle","<name>",entity)` over hand-authoring `entity_template`. The
  in-scene composer wires `__ref` shapes correctly and pulls in every
  transitively-needed asset.
- Keep bundles focused — one prefab per bundle. If you have a "vehicle
  + driver + sound" assembly, split into three bundles and reference
  them from a scene rather than packing all three into one bundle.
- Name child entities meaningfully — bundle interiors are
  inspectable, and stable IDs let other tooling (`entity_template`
  edits, animation hooks) find them.
- Sub-bundles compose: a bundle's `entity_template` can `__ref` a
  child `.bundle/`. The asset registry resolves them transitively.

## Common pitfalls

- **Confusing the two spawn paths.** `Asset` component = live link,
  reference-only scene save. `bundleRef:instantiate()` directly = baked,
  full hierarchy in `scene.json`.
- **Inner-ref guids drift.** If you copy an `entity_template` from
  one bundle into another, the `__ref` guids may not resolve —
  re-compose from a live scene rather than copy-pasting JSON.
- **The `.bundle` suffix is part of the identity.** This is the
  container-category convention. Don't strip it in reference strings.
- **Live editing only fires for live-linked carriers.** Already-baked
  entities don't update when you edit the source bundle.

## Related types

- `Asset.component` — the canonical mount point for a live-linked
  bundle. See its README for the `idMap` / `diff` machinery in
  detail.
- `.scene` — owns a whole world's worth of entities; spawns bundles
  into itself via `Asset` components on per-spot entities.
