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
Signals
Reactive signals
Derived values
Channels
Groups
Overlay and offset signals
Watch
Events and subscribe
The decision, in one pass
Ask these in order. The first yes is your answer.
| Question | Primitive |
|---|---|
| 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 writes | signal |
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”.
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.