---
title: "avatar"
description: "A playable character, composed from three independent parts:"
section: "Types"
slug: "types-avatar"
canonical: "https://origozero.ai/docs/types-avatar"
updated: "2026-07-31T07:12:26.483313724+00:00"
tags: ["asset-type", "reference"]
---

# avatar

- **body** — a `.bundle`: a skinned `.mesh` + bone entities (a character imported
  from a GLB/FBX). The look.
- **controller** — the movement system. Default: the standard humanoid kit
  (`CharacterController` + `MovementState` + `Humanoid`).
- **animation** — the system that animates the body. Default: the shared
  `Locomotion` blend space (reads the controller's velocity, plays retargeted
  clips). Or `ClipPlayer`. Or your own component.

Movement and animation are **not** tied together — attaching the controller never
pulls in `Locomotion`, and choosing your own `animation` keeps the standard
controller. Authoring your own locomotion is a one-field swap (`animation = ...`).
Because the standard clip set retargets onto **any** humanoid rig, the default
locomotion drives any humanoid body with no per-body setup and no editing of any
built-in content.

## Make an imported character playable

```luau
-- After importing a rigged humanoid GLB, the importer leaves a `.bundle`
-- (skinned mesh + bones). Survey for it, then wrap it in an avatar:
local body = asset.resolve("my_character", "bundle")
local hero = asset.create("avatar", "my_hero", { body = body })
```

To make `hero` the body every connecting player spawns with, point the scene's
`PlayerPrototype.body` at a body entity whose `Asset` source is `hero` (authored
in the scene tree — see `man scene`). To drive the **local** player with it right
now, instantiate it and bind the entity:

```luau
local root = hero:instantiate()                  -- spawns the body + movement kit
layers.active.players.localPlayer.avatar = root  -- bind the live body
```

An avatar is **not** humanoid-only. The body can be anything — a humanoid, a
vehicle, an unrigged plane with a custom controller and shader. The defaults
adapt to the body: a **humanoid** body defaults to the shared humanoid Locomotion
(the `synty` clip preset); a **non-humanoid / unrigged** body gets **no
auto-animation** (you supply your own `controller` and `animation`). This is the
non-humanoid detection — an arbitrary animated model is never auto-driven by
humanoid locomotion — and it never blocks creating the avatar.

```luau
-- Non-humanoid avatar (e.g. a custom-controlled body), fully generic:
asset.create("avatar", "ship", { body = shipBundle, controller = { system = "ShipController" }, animation = false })
```

## Options

`asset.create("avatar", name, opts)`:

| opt | meaning |
|-----|---------|
| `body` (required) | the body `.bundle` ref |
| `controller` | movement: `true`/nil = standard kit, `false` = none, or a component name / `{ system, data }` |
| `animation` | a component name or `{ system, ... }` — **author your own locomotion here**; defaults to Locomotion |
| `locomotion` | clip preset for the default Locomotion (default `"synty"`) |
| `clip` | a single `.animation` ref — selects a one-clip `ClipPlayer` |
| `looping` / `speed` / `sourceRig` | `ClipPlayer` tuning (with `clip`) |

Author your own locomotion (keeps the standard movement):

```luau
asset.create("avatar", "my_hero", { body = body, animation = { system = "MyLocomotion" } })
```

`avatar.json`:

```json
{
  "body": { "__ref": "<bundle guid>" },
  "controller": true,
  "animation": { "system": "Locomotion", "preset": "synty" }
}
```

The body's facing is read from its rig (the importer records a `forward` for each
body — `{0,0,1}` for a +Z Synty/FBX body, `{0,0,-1}` for a -Z glTF body), so
Locomotion turns the body to its movement direction regardless of how its source
authored it. No per-avatar facing knob.

## Use

```luau
local root = avatarRef:instantiate()                              -- spawn body + movement kit
avatarRef:setCharacter(root, asset.resolve("knight", "bundle"))  -- swap the body, keep the behaviour
```

`:instantiate()` spawns the body and attaches the movement system bound to it.
`:setCharacter(root, body)` despawns the current body and spawns a new one in its
place; the Locomotion kit on the avatar root re-resolves the new body's rig, so
the look swaps while the behaviour is kept.
