---
title: Reactivity
description: Where each of an actor's values comes from, and the primitive that declares it.
sidebar:
  hidden: true
---

An actor declares its values as fields, and the primitive behind each field says where the
value comes from. The scene writes signals. A `derived` is computed from other reads each
time a frame resolves. A channel is published from outside the scene while frames play,
such as the box a renderer measures. Events carry an actor's intent to the
actors that subscribe, and a watch runs code when one of the actor's own values changes.

```ts twoslash title="player.ts"
import {
  derived,
  group,
  LayoutActor,
  signal,
  watch,
} from "@motionactor/core";

type PlayerEvents = { ended: { at: number } };

export class Player extends LayoutActor<PlayerEvents> {
  // Written by the scene.
  time = signal(0);
  length = signal(10);
  controls = group({ visible: signal(true) });

  // Computed when a frame resolves.
  progress = derived(() => this.time() / this.length());
  thumbX = derived(() => this.progress() * this.localLayout().width);

  // Runs while the scene compiles.
  hideControls = watch(
    () => this.time() > 0,
    (playing) => this.controls.visible.set(!playing),
  );

  *play() {
    yield* this.time.tween(this.length(), this.length());
    yield* this.emit.ended({ at: this.time() });
  }
}
```

## Choosing a primitive

Ask in order and stop at the first yes.

| Question | Primitive |
| --- | --- |
| Is it computed from other values and never set? | [`derived`](/docs/reactivity/derived) |
| Does the host publish it while frames play, such as a measured box? | [`channel`](/docs/reactivity/channel) |
| Is it one nested object whose leaves animate separately? | [`reactiveSignal`](/docs/reactivity/reactive-signal) |
| Does it change how the actor looks, such as a shake or a glow? | an [effect](/docs/rendering/effects) |
| Does the scene set or animate it? | [`signal`](/docs/reactivity/signal) |

Two primitives hold no values. [`group`](/docs/reactivity/group) names a set of fields, as
`layout` names `x`, `y`, `width` and `height`. [Events](/docs/reactivity/events-and-subscribe)
tell other actors that something happened.

A signal's history is in the scene: every `set` and `tween` is recorded at a frame, and
seeking replays the record. A channel has no history in the scene, only what was published.
A value the scene cannot know, such as the width a browser lays a label out at, has to be a
channel. Modelled as a signal it would be fixed at compile time and wrong in any other
viewport.

## Who writes a value

An actor writes its own signals. Another actor that wants a change emits an event and lets
the owner decide what the change means:

```ts twoslash
import { LayoutActor, signal, subscribe } from "@motionactor/core";

type ButtonEvents = { pressed: { via: "key" | "pointer" } };

class Button extends LayoutActor<ButtonEvents> {
  *press(via: "key" | "pointer") {
    yield* this.emit.pressed({ via });
  }
}

class Video extends LayoutActor {
  time = signal(12);

  *restart() {
    yield* this.time.tween(0, 0.3);
  }
}

declare const video: Video;
declare const button: Button;
// ---cut---
subscribe(video, button, {
  *pressed() {
    yield* this.restart();
  },
});
```

`Video` keeps every rule about its own time in `restart`. The button knows nothing about
videos, so the same button can restart a video in one scene and submit a form in another.
