Reactivity

Writable state, computed reads, host-published values and coordination are separate primitives. Picking the right one is most of actor design.

The runtime deliberately refuses to give you one general-purpose reactive box. Each primitive encodes a different claim about where a value comes from and who is allowed to change it — which is what keeps frame resolution pure.

Choosing a primitive

The decision, in one pass

Ask these in order. The first yes is your answer.

QuestionPrimitive
Is the value computed from other reads and never set directly?derived
Is the value observed rather than authored — measured, fetched, simulated, sensed?channel
Is it a nested bag whose individual leaves need to animate on their own?reactiveSignal
Is it transient motion sitting on top of a canonical value?overlaySignal
Anything else the timeline writessignal

Two of these are not state at all. group is namespacing, and events are coordination — reach for them when the question is "how do I organize this" or "how does another actor find out", not "where does this value live".

The signal / channel line

If the value's history is authoredset, tween, play — it is a signal. If the value is observed — measured, fetched, simulated, sensed — it is a channel. This is the one distinction worth memorizing; almost every determinism bug is a channel value that was modelled as a signal.

Ownership

Owners mutate their own canonical state. Non-owners emit semantic intents and let the owner decide.

// Wrong — reaching through another actor to write its state.
playButton.onPress(() => video.playback.currentTime.set(0));

// Right — emit intent, the owner responds.
subscribe(video, playButton, {
  *pressed() {
    yield* this.restart();
  },
});

That rule is what lets an actor be reused: it keeps every write to a piece of state inside the class that declared it, so its invariants hold no matter who is driving.

On this page