# Object3DBehaviour (/docs/api/object3d-behaviour)





`Object3DBehaviour` is the base class for all components. Extend it, override the lifecycle methods you need, and attach it to any `Object3D` via [`addComponent`](/docs/api/operations). The component hooks itself into the loop and the lifecycle automatically — the shared [`ThreeContext`](/docs/api/three-context) is available as `this.ctx`, and any registered [`ContextModule`](/docs/api/context-module) via `this.modules`.

```ts
import { Object3DBehaviour, addComponent } from "three-start";

class Spin extends Object3DBehaviour {
  onAwake() {
    // initialize
  }
  onUpdate() {
    this.object.rotation.y += this.ctx.getDeltaTime();
  }
  onDestroy() {
    // clean up
  }
}

addComponent(mesh, Spin);
```

First activation order: `onAwake` → `onEnable` → `onStart`

Enable/disable cycle: `onEnable` ↔ `onDisable`

On removal: `onDisable` → `onDestroy`

Properties [#properties]

| Name | Type | Description |
|---|---|---|
| `object` | `THREE.Object3D` | The Three.js `Object3D` this component is attached to. Available from `onAwake` onwards. |
| `ctx` | `ThreeContext` | The shared [`ThreeContext`](/docs/api/three-context) runtime. Available from `onAwake` onwards. |
| `modules` | `ThreeStartModules` | Shortcut to registered [`ContextModule`](/docs/api/context-module) instances: `this.modules.myModule`. |
| `enabled` | `boolean` | `true` while the component is enabled. Toggle with `enable()` / `disable()` / `setEnabled()`. |

Methods [#methods]

| Name | Type | Description |
|---|---|---|
| `enable` | `() => void` | Enable the component. Fires `onEnable` if the owning object is active and the context is available. |
| `disable` | `() => void` | Disable the component. Fires `onDisable` if it was active and unsubscribes per-frame hooks. |
| `setEnabled` | `(enabled: boolean) => void` | Set the enabled flag explicitly. No-op if the value is unchanged. |

Override methods [#override-methods]

These are the lifecycle hooks you implement in your subclass.

<Callout type="info">
  Per-frame hooks (`onUpdate`, `onBeforeRender`, `onAfterRender`) are subscribed to the runtime loop **only if you override them**. An empty component pays nothing for dispatch.
</Callout>

| Name | Type | Description |
|---|---|---|
| `onAwake` | `() => void` | `@lifecycle` `@override` Called once when the component first becomes active. Use for one-time initialization. Runs before `onEnable` and `onStart`. |
| `onStart` | `() => void` | `@lifecycle` `@override` Called once after `onEnable`, on the first activation. Runs after all modules and sibling components have awakened. |
| `onEnable` | `() => void` | `@lifecycle` `@override` Called every time the component transitions from disabled → enabled. Also runs on the first activation (after `onAwake`). |
| `onDisable` | `() => void` | `@lifecycle` `@override` Called every time the component transitions from enabled → disabled, and before `onDestroy`. |
| `onUpdate` | `() => void` | `@lifecycle` `@override` Called once per frame while the component is active. Override to run per-frame logic. |
| `onBeforeRender` | `() => void` | `@lifecycle` `@override` Called once per frame, before the scene is rendered. Override to run pre-render logic. |
| `onAfterRender` | `() => void` | `@lifecycle` `@override` Called once per frame, after the scene is rendered. Override to run post-render logic. |
| `onDestroy` | `() => void` | `@lifecycle` `@override` Called when the component is destroyed. Use to dispose resources, cancel requests, or remove external listeners. |

Events [#events]

`Object3DBehaviour` extends [`TypedEmitter`](/docs/api/typed-emitter) — declare your own event map via the generic parameter to expose typed events on instances of your subclass.

**Emitter methods**

| Name | Type | Description |
|---|---|---|
| `on` | `<K extends keyof TEvents>(event: K, fn: (...args: TEvents[K]) => void, context?: any) => this` | Subscribe to an event declared via the class generic parameter. |
| `once` | `<K extends keyof TEvents>(event: K, fn: (...args: TEvents[K]) => void, context?: any) => this` | Subscribe to an event, automatically unsubscribing after it fires once. |
| `off` | `<K extends keyof TEvents>(event: K, fn?: (...args: TEvents[K]) => void, context?: any, once?: boolean) => this` | Remove a previously registered listener. Omit `fn` to remove all listeners for the event. |

Declaring events in your subclass [#declaring-events-in-your-subclass]

Pass a typed event map as the generic parameter. Keys become the `event` argument in `on` / `off` / `once`, and tuple values type the listener payload. `emit` is `protected` — only the component itself can fire its own events.

```ts
type HealthEvents = {
  damaged: [amount: number, source: string];
  died: [];
};

class Health extends Object3DBehaviour<HealthEvents> {
  hp = 100;

  takeDamage(n: number, by: string) {
    this.hp -= n;
    this.emit("damaged", n, by);          // ✅ inside the class
    if (this.hp <= 0) this.emit("died");  // ✅
  }
}

// Subscribe from outside:
const health = getComponent(player, Health)!;
health.on("damaged", (amount, by) => console.log(`-${amount} from ${by}`));
health.emit("damaged", 10, "x");          // ❌ compile error: emit is protected
```

The default for the generic is `{}` — without declaring events, calling `on` / `emit` fails at compile time (`keyof {} = never`).
