---
title: signal
description: A value the scene writes. set, tween, play, animate, transition and derive record each write at the cursor.
mode: wide
---

A signal is a field whose value the scene records over time. Call it to read it. Its methods
write, and every write is recorded at the cursor, so the value at any frame is whatever the
record says at that frame.

```ts twoslash title="status-badge.ts"
import { LayoutActor, signal } from "@motionactor/core";

export class Badge extends LayoutActor {
  label = signal("Ready");
  tone = signal<"neutral" | "success">("neutral");
  count = signal(0);
}
```

```ts twoslash
// @filename: status-badge.ts
import { LayoutActor, signal } from "@motionactor/core";
export class Badge extends LayoutActor {
  label = signal("Ready");
  tone = signal<"neutral" | "success">("neutral");
  count = signal(0);
}
// @filename: status-badge.scene.ts
// ---cut---
import { createScene } from "@motionactor/core";
import { Badge } from "./status-badge";

const compiled = createScene(function* (ctx) {
  const badge = ctx.spawn(Badge, { label: "Go" });
  yield* badge.count.tween(100, 1);
  badge.label.set("Done");
  badge.tone.set("success");
  yield* ctx.wait(0.5);
  return { badge };
}).build({ viewport: { width: 1920, height: 1080 }, fps: 30 });

compiled.at(15).scene.badge?.count; // 50
compiled.at(15).scene.badge?.label; // "Go"
compiled.at(30).scene.badge?.label; // "Done"
```

`badge.count()` inside the scene reads the value at the cursor. Inside a
[`derived`](/docs/reactivity/derived) it reads the value at the frame being resolved. `peek()`
is the same read.

## Declaring

`signal(initial)` takes its type from the initial value. Pass the type when the initial value
is narrower than the values the field will hold: `signal<"neutral" | "success">("neutral")`,
or `signal<string[]>([])`, which would otherwise be `never[]`.

Every signal is also a spawn input, so `ctx.spawn(Badge, { label: "Go" })` starts the badge
with that label. A signal declared without an initial value is required:

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

class Title extends LayoutActor {
  text = signal<string>();
}

createScene(function* (ctx) {
  ctx.spawn(Title, { text: "Chapter one" });
}).build({ viewport: { width: 1920, height: 1080 }, fps: 30 });
```

Spawning `Title` without `text` is a type error, and at run time it throws
`Title: text is required`.

`signal.of(type, initial)` declares a field whose value has a named type, such as
`unitInterval`, `colorType` or an asset type like `image`. Reflection reports the type, so an
editor can offer the right control, and spawn checks inputs against the type's schema. A
color field also accepts a CSS string and stores the parsed color.
[Media, assets and fonts](/docs/rendering/media-assets-and-fonts) covers the asset types.

## Writing

| Method | Takes time | Writes |
| --- | --- | --- |
| `set(value)` | no | The value, from the cursor on |
| `tween(target, timing)` | yes | A move from the current value to `target` |
| `play({ to, ... })` | yes | A tween with repeats, direction, delay and fill |
| `animate(options)` | no | A `play` that runs beside the script |
| `transition(fn, timing)` | yes | Whatever `fn` returns for each frame's progress |
| `derive(fn)` | no | A binding to a computation, from the cursor on |

A method that takes time is a generator, and the scene waits for it with `yield*`. The others
return at once and the cursor stays where it was.

<SignalMotionScene />

The top dot tweens once. The middle one plays the same range four times, alternating
direction. The bottom one starts a scale `animate` and then a tween: `animate` takes no time,
so both begin on the same frame.

### Timing

`timing` is required: a number of seconds, or an object with a `duration`.

| Option | |
| --- | --- |
| `duration` | Seconds. Required |
| `easing` | A function from progress to eased progress, such as `Easing.outCubic` |
| `spring` | `{ stiffness, damping, mass }`, a spring curve across `duration`, such as `SpringPresets.bouncy` |
| `from` | The start value, in place of the current one |
| `space` | `"srgb"` or `"oklch"`, for tweens between color strings |

Seconds become whole frames at the build's `fps`. A spring runs inside the duration and lands
on the target at its last frame. Between the endpoints, a spring or an easing such as `wiggle`
can carry a number past its target and back; a color stays between its two endpoints.
`SpringPresets.gentle` peaks 3% past the target, `stiff` 11%, `bouncy` 21% and `wobbly` 30%;
`heavy` does not overshoot.

### play

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

class Dot extends LayoutActor {
  x = signal(0);
}

createScene(function* (ctx) {
  const dot = ctx.spawn(Dot);
  // ---cut---
  yield* dot.x.play({
    to: 100,
    duration: 0.5,
    iterations: 2,
    direction: "alternate",
    delay: 0.2,
    fill: "forwards",
  });
}).build({ viewport: { width: 1920, height: 1080 }, fps: 30 });
```

`direction` is `"normal"`, `"reverse"`, `"alternate"` or `"alternate-reverse"`, as in CSS.
`fill` decides what holds outside the active span: `"backwards"` shows the first value during
`delay`, `"forwards"` keeps the last value after the end, `"both"` does both and `"none"`
returns to the underlying value. The default is `"forwards"`.

### animate

`animate` takes the options `play` takes and records the same motion on a branch of its own.
The script carries on from the same frame. A finite `animate` that outlasts the script keeps
the scene going until it ends. One with `iterations: Infinity` runs until the scene ends and
does not make it longer.

Starting another anonymous `animate` on the same signal replaces the running one. Named
animations, `animate("pulse", options)`, replace an animation with the same name; starting
one while an animation with another name is running on the signal throws. A `tween` or
`play` on the signal stops the animation, and so does the actor's exit.

### transition

`transition` calls `fn` with the eased progress and the value the signal had when the
transition started. Use it for values a tween cannot interpolate:

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

class Caption extends LayoutActor {
  text = signal("Backspace");
}

const compiled = createScene(function* (ctx) {
  const caption = ctx.spawn(Caption);
  yield* caption.text.transition(
    (progress, from) =>
      from.slice(0, Math.round(from.length * (1 - progress))),
    1,
  );
  return { caption };
}).build({ viewport: { width: 1920, height: 1080 }, fps: 30 });

compiled.at(15).scene.caption?.text; // "Backs"
```

A list that changes as a whole, such as a set of windows opening at once, can be one
`signal<Window[]>` written by a `transition` that returns the new array each frame. When
separate leaves of a nested value move on their own schedules, use a
[`reactiveSignal`](/docs/reactivity/reactive-signal).

### derive

`derive` binds the signal to a computation from the cursor on. It is how one actor follows
another without either owning the other's state:

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

class Dot extends LayoutActor {
  x = signal(0);
}

const compiled = createScene(function* (ctx) {
  const leader = ctx.spawn(Dot);
  const follower = ctx.spawn(Dot);
  follower.x.derive(() => leader.x() + 10);
  yield* leader.x.tween(100, 1);
  return { follower };
}).build({ viewport: { width: 1920, height: 1080 }, fps: 30 });

compiled.at(30).scene.follower?.x; // 110
```

A later `set` or `tween` on the follower replaces the binding.

## Several at once

The verbs that take time are generators, so the scene's helpers compose them: `ctx.all(...)`
runs several together and `ctx.sequence(delay, ...)` starts each one `delay` seconds after
the one before. Inside a command the same helpers are on `this.runtime`.
[Scene orchestration](/docs/runtime/scene-orchestration) covers them.

Writing to a signal while a frame resolves, in a `derived` body for example, throws
`Cannot set signal "x" during signal resolution`.
