Scenario aspects — ready-made behaviors
Five aspects that cover the basic "make the scene DO something" scenarios without writing code:
travel to a point, run a waypoint circuit, spin, face a target, start a GLB animation. They're
designed for the visual scene editor (attach from the Aspects menu, wire targets by picking nodes
in the viewport), but they're ordinary aspects — node.aspect(MoveTo, { target }) from hand
code works the same.
All motion runs per-frame in play mode. While a scene is being edited, aspects are inert — but the movement aspects draw their paths as editor-only lines (blue for movement, amber for look-at), so you can see and tune a route without running the scene.
The usual recipe pairs them with Empty nodes (plain group nodes — see scene-files.md): place an Empty where something should go, then point the aspect at it. On a camera node, the same aspects become camera moves.
MoveTo
Travel from the node's starting position to a target node.
| Field | Default | Meaning |
|---|---|---|
target |
null |
Destination node (ref('name') in scene files) |
duration |
2 |
Seconds for the full trip |
delay |
0 |
Seconds to wait before starting |
mode |
'once' |
'once' stop there · 'loop' restart from the start point · 'pingpong' back and forth |
easing |
'smooth' |
'smooth' ease-in-out · 'linear' constant speed |
The target is tracked live — if it moves, the trip bends toward its new position (and once
keeps the node pinned to it after arriving).
crate: {
mesh: { kind: 'box', size: [1, 1, 1] },
position: [0, 0.5, 0],
aspects: [use(MoveTo, { target: ref('dropPoint'), duration: 3, mode: 'pingpong' })],
},
dropPoint: { position: [4, 0.5, -2] }, // an EmptyFollowPath
Travel through the children of a path node, in scene-file order — add an Empty per waypoint.
| Field | Default | Meaning |
|---|---|---|
path |
null |
The node whose children are the waypoints |
duration |
6 |
Seconds for one full pass |
delay |
0 |
Seconds to wait before starting |
mode |
'loop' |
'loop' closed circuit (last → first) · 'once' stop at the last point · 'pingpong' back and forth along the open path |
orient |
true |
Turn to face the direction of travel |
forward |
'-z' |
Which local axis leads when orienting |
Speed is constant along the whole path (segment lengths are measured live, so dragging a waypoint
retimes the run correctly). GLB models usually face +z — flip forward if your model drives
backwards.
bus: {
model: asset('./bus.glb'),
aspects: [use(FollowPath, { path: ref('route'), duration: 12, forward: 'z' })],
},
route: {
position: [0, 0, 0],
children: {
stop1: { position: [0, 0, 0] },
stop2: { position: [8, 0, 2] },
stop3: { position: [5, 0, 8] },
},
},Spin
Continuous rotation about a local axis — speed degrees per second, axis 'x' | 'y' | 'z'
(default 'y'). Composes in quaternion space, so it's correct on pre-rotated nodes.
LookAt
Keep the node facing a target node each frame.
| Field | Default | Meaning |
|---|---|---|
target |
null |
The node to face |
forward |
'-z' |
Which local axis points at the target |
smoothing |
0 |
0 snaps instantly; otherwise seconds of turn lag (bigger = slower) |
A turret tracking the player, a signpost, or — on a camera node — a camera that keeps a moving
hero framed while FollowPath drives the crane.
PlayAnimation
Start a GLB clip when the scene runs. Attach to a model: node.
| Field | Default | Meaning |
|---|---|---|
clip |
'' |
Clip name; empty = the model's first clip |
loop |
true |
Loop the clip |
speed |
1 |
Playback rate |
In the editor its inspector card lists the model's clips in a dropdown and can Preview/Stop
the clip live while editing. For runtime control (switching clips from your own code, seek, events)
use model.anim directly — this aspect only presses play at load.