Skip to content

Custom Behaviours ​

A Behaviour is a user-authored TypeScript class that runs per-frame logic on a single entity. Behaviours are attached to entities through the ScriptComponent, and are dispatched each frame by the ScriptBehaviourSystem.

Anatomy of a script ​

ts
import {
  Behaviour,
  type FloatInput,
  type DropdownInput,
} from "@relu-interactives/spatial-ecs";

export default class Spinner extends Behaviour {
  data = {
    speed: 1 as FloatInput,
    axis: {
      value: "y",
      options: [
        { label: "X", value: "x" },
        { label: "Y", value: "y" },
        { label: "Z", value: "z" },
      ],
    } as DropdownInput,
  };

  init() {
    // Called once before the first update().
  }

  onUpdate() {
    // Called every frame while the script is enabled.
    const axis = this.data.axis.value as "x" | "y" | "z";
    this.transform.rotation[axis] += this.data.speed * this.deltaTime;
  }

  dispose() {
    // Optional cleanup when the entity is destroyed
    // or the script is detached.
  }
}

IMPORTANT

For the runtime to recognize the class make sure the default export of the class subclasses Behaviour.

Available context ​

When init() / onUpdate() runs, the following fields are populated:

FieldDescription
this.worldThe owning World.
this.entityIdNumeric entity id.
this.entityA WorldEntityView for the entity (id + components).
this.transformMutable TransformComponent (position/rotation/scale).
this.threeRefThe Object3DRef wrapping the live three.js object.
this.dataThe ScriptComponent.data payload (your inspector fields).
this.deltaTimeSeconds since last frame.

Helper methods ​

Behaviour exposes a small ergonomics surface so scripts rarely need to touch three.js directly:

See the full Behaviour API.

Lifecycle hooks ​

HookWhen
init()Once, before the first onUpdate().
onUpdate()Every frame while enabled.
dispose()When the script is detached or the entity destroyed.
onSelect() / onDeselect()Events fired when a user clicks or taps an object in the scene.
onTargetFound() / onTargetLost()Image-tracking target visibility changed.

update(deltaTime) is implemented by the base class, do not override it. Override onUpdate() instead.

Where to put scripts ​

In a Relu Spatial project, scripts live under the project's assets/ directory. The editor compiles them through esbuild and exposes them via the script registry.

Inspector data binding ​

The data object is the bridge between the editor inspector and your script. Use typed input annotations FloatInput, ColorInput, DropdownInput, etc. to control which inspector control renders for each field:

ts
import {
  Behaviour,
  type FloatInput,
  type DropdownInput,
} from "@relu-interactives/spatial-ecs";

export default class Spinner extends Behaviour {
  data = {
    speed: 1 as FloatInput,
    axis: {
      value: "y",
      options: [
        { label: "X", value: "x" },
        { label: "Y", value: "y" },
        { label: "Z", value: "z" },
      ],
    } as DropdownInput,
  };

  onUpdate() {
    const axis = this.data.axis.value as "x" | "y" | "z";
    this.transform.rotation[axis] += this.data.speed * this.deltaTime;
  }
}

See Behaviour Data Binding for the full reference of all input types (TextInput, ColorInput, Vector2Input, Vector3Input, SliderInput, CheckboxInput, ToggleInput, AssetInput, and more).

See Accessing Components to read and mutate built-in components such as PhysicsComponent, AudioComponent, AnimationComponent, LightComponent, and MaterialComponent from script.

See Reading Input for the full keyboard and mouse input API, KeyboardKeys reference, and scripting examples.

Behaviours run at preview/runtime only ​

Behaviour scripts execute only at preview and runtime, never inside the editor. Gameplay logic, input handling, physics, and anything that should not run while authoring belongs in a Behaviour.

For a visual that must render and animate inside the editor (custom shaders, procedural geometry, grass, particle systems, anything the object palette can't produce), use a Script Object instead. A ScriptObject builds a THREE.Object3D that renders in the editor AND at runtime.