Skip to content
MotionActor
Esc
↑↓navigate↵open⌘Jpreview

Scene orchestration

A scene script runs once, moving a cursor through time while it spawns actors and records their motion.

A scene is a generator function. createScene declares it; each scene.build({ viewport, fps }) runs it once, and every verb it calls writes to a timeline record at the current position of a cursor. A yield* moves the cursor forward. Nothing here draws: a host later asks the compiled scene for a frame and gets back the state the record describes.

import { function createScene<TSceneExports, const I extends SceneInputs = Readonly<Record<never, SceneInput>>, const L extends SceneLayers = Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<I, L, TSceneExports>, options?: SceneDefinitionOptions<I, L>): SceneDefinition<I, L, TSceneExports>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
, class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor,
function signal<T>(): RequiredSignal<T> (+1 overload)
module signal
A timeline value on an actor, and an input to every spawn of it. Without an initial value the signal is required: `ctx.spawn` must supply it.
signal
} from "@motionactor/core";
class class CardCard extends class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor { Card.label: ActorFieldSignal<string>label = signal<string>(initial: string): ActorFieldSignal<string> (+1 overload)
A timeline value on an actor, and an input to every spawn of it. Without an initial value the signal is required: `ctx.spawn` must supply it.
signal
("");
} export const
const compiled: CompiledScene<{
    card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled
=
createScene<{
    card: Card;
}, Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
    card: Card;
}>, options?: SceneDefinitionOptions<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>> | undefined): SceneDefinition<...>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
(function* (ctx: SceneContextctx) {
const const card: Cardcard = ctx: SceneContextctx.
CompileContext.spawn<Card>(definition: ActorClass<Card>, overrides?: {
    measuredBox?: undefined;
    label?: SignalInputValue<string> | undefined;
    appearance?: {
        opacity?: SignalInputValue<number> | undefined;
    } | undefined;
    layout?: {
        x?: SignalInputValue<LayoutValue> | undefined;
        y?: SignalInputValue<LayoutValue> | undefined;
        width?: Readonly<{
            kind: "layout.percent";
            value: number;
        }> | SignalInputValue<number | `${number}%` | Readonly<{
            kind: "layout.percent";
            value: number;
        }>> | undefined;
        height?: Readonly<...> | ... 1 more ... | undefined;
    } | undefined;
    ... 19 more ...;
    parent?: undefined;
} | undefined, options?: {
    ...;
} | undefined): Card (+1 overload)
spawn
(class CardCard, {
label?: SignalInputValue<string> | undefinedlabel: "Hello",
appearance?: {
    opacity?: SignalInputValue<number> | undefined;
} | undefined
appearance
: { opacity?: SignalInputValue<number> | undefinedopacity: 0 },
}); yield* const card: Cardcard.
LayoutActor<{}>.appearance: {
    opacity: TypedFieldSignal<number, number>;
} & SignalGroupDefinition
appearance
.opacity: TypedFieldSignal<number, number>opacity.function tween(target: number, options: number | SignalTweenOptions<number>): Generator<Frame, void, unknown>
Tween to target over authored seconds — must yield*
tween
(1, 0.3);
yield* ctx: SceneContextctx.CompileContext.wait(duration: Duration): Generator<Frame, void, unknown>wait(1); yield* const card: Cardcard.
LayoutActor<{}>.layout: {
    x: ActorFieldSignal<LayoutValue>;
    y: ActorFieldSignal<LayoutValue>;
    width: TypedFieldSignal<number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>, number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>>;
    height: TypedFieldSignal<number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>, number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>>;
} & SignalGroupDefinition
layout
.x: ActorFieldSignal<LayoutValue>x.LiveSignal<LayoutValue>.tween(target: LayoutValue, options: number | SignalTweenOptions<LayoutValue>): Generator<Frame, void, unknown>
Tween to target over authored seconds — must yield*
tween
(200, 0.5);
return { card: Cardcard }; }).
SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, { ...; }>.build(args: SceneBuildInput<Readonly<Record<never, SceneInput>>>): CompiledScene<{
    card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
build
({ viewport: SceneViewportviewport: { SceneViewport.width: numberwidth: 1920, SceneViewport.height: numberheight: 1080 }, fps: numberfps: 30 });
const compiled: CompiledScene<{
    card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled
.CompiledScene<{ ...; }, CompiledLayersOf<...>>.totalDuration: numbertotalDuration; // 54
const compiled: CompiledScene<{
    card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled
.
CompiledScene<{ ...; }, CompiledLayersOf<...>>.at(frame: Frame, options?: FrameResolutionOptions): FrameState<{
    card: Card;
}>
at
(9).
FrameState<{ ...; }>.scene: {
    card: ResolvedActorOfInstance<Card> | undefined;
}
scene
.card: ResolvedActorOfInstance<Card> | undefinedcard?.
appearance: {
    opacity: number;
}
appearance
.opacity: number | undefinedopacity; // 1

Durations are seconds. A build converts them to frames at the fps its host passes, rounding each one to the nearest frame. Built at 30 fps, 0.3 s is 9 frames, the wait is 30 and the slide is 15, so the scene is 54 frames long.

The cursor

ctx.cursor is the cursor in frames and ctx.cursorSeconds the same position in seconds. Every timing verb is a generator that starts its branches at the cursor and moves it on when you yield* it:

Verb Branches start Cursor moves by
wait(s) s
all(...branches) All at the cursor The longest branch
sequence(d, ...branches) Branch i at i × d Until the last branch ends
chain(...branches) Each when the one before ends The sum
delay(d, branch) After d d plus the branch
import { function createScene<TSceneExports, const I extends SceneInputs = Readonly<Record<never, SceneInput>>, const L extends SceneLayers = Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<I, L, TSceneExports>, options?: SceneDefinitionOptions<I, L>): SceneDefinition<I, L, TSceneExports>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
, class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor } from "@motionactor/core";
class class DotDot extends class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor {} createScene<void, Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>, options?: SceneDefinitionOptions<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>> | undefined): SceneDefinition<...>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
(function* (ctx: SceneContextctx) {
const const dots: Dot[]dots = [0, 1, 2].Array<number>.map<Dot>(callbackfn: (value: number, index: number, array: number[]) => Dot, thisArg?: any): Dot[]
Calls a defined callback function on each element of an array, and returns an array that contains the results.
@paramcallbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.@paramthisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
map
(() => ctx: SceneContextctx.
CompileContext.spawn<Dot>(definition: ActorClass<Dot>, overrides?: {
    measuredBox?: undefined;
    layout?: {
        x?: SignalInputValue<LayoutValue> | undefined;
        y?: SignalInputValue<LayoutValue> | undefined;
        width?: Readonly<{
            kind: "layout.percent";
            value: number;
        }> | SignalInputValue<number | `${number}%` | Readonly<{
            kind: "layout.percent";
            value: number;
        }>> | undefined;
        height?: Readonly<{
            kind: "layout.percent";
            value: number;
        }> | SignalInputValue<number | `${number}%` | Readonly<...>> | undefined;
    } | undefined;
    ... 20 more ...;
    parent?: undefined;
} | undefined, options?: {
    ...;
} | undefined): Dot (+1 overload)
spawn
(class DotDot));
yield* ctx: SceneContextctx.CompileContext.all(...generators: Generator<Frame, unknown, unknown>[]): Generator<Frame, void, unknown>all(...const dots: Dot[]dots.Array<Dot>.map<Generator<number, void, unknown>>(callbackfn: (value: Dot, index: number, array: Dot[]) => Generator<number, void, unknown>, thisArg?: any): Generator<number, void, unknown>[]
Calls a defined callback function on each element of an array, and returns an array that contains the results.
@paramcallbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.@paramthisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
map
((d: Dotd) => d: Dotd.
LayoutActor<{}>.layout: {
    x: ActorFieldSignal<LayoutValue>;
    y: ActorFieldSignal<LayoutValue>;
    width: TypedFieldSignal<number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>, number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>>;
    height: TypedFieldSignal<number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>, number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>>;
} & SignalGroupDefinition
layout
.y: ActorFieldSignal<LayoutValue>y.LiveSignal<LayoutValue>.tween(target: LayoutValue, options: number | SignalTweenOptions<LayoutValue>): Generator<Frame, void, unknown>
Tween to target over authored seconds — must yield*
tween
(100, 0.5)));
ctx: SceneContextctx.CompileContext.cursor: numbercursor; // 15 yield* ctx: SceneContextctx.CompileContext.sequence(delay: Duration, ...generators: Generator<Frame, unknown, unknown>[]): Generator<Frame, void, unknown>sequence( 0.1, ...const dots: Dot[]dots.Array<Dot>.map<Generator<number, void, unknown>>(callbackfn: (value: Dot, index: number, array: Dot[]) => Generator<number, void, unknown>, thisArg?: any): Generator<number, void, unknown>[]
Calls a defined callback function on each element of an array, and returns an array that contains the results.
@paramcallbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.@paramthisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
map
((d: Dotd) => d: Dotd.
LayoutActor<{}>.layout: {
    x: ActorFieldSignal<LayoutValue>;
    y: ActorFieldSignal<LayoutValue>;
    width: TypedFieldSignal<number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>, number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>>;
    height: TypedFieldSignal<number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>, number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>>;
} & SignalGroupDefinition
layout
.y: ActorFieldSignal<LayoutValue>y.LiveSignal<LayoutValue>.tween(target: LayoutValue, options: number | SignalTweenOptions<LayoutValue>): Generator<Frame, void, unknown>
Tween to target over authored seconds — must yield*
tween
(0, 0.5)),
); ctx: SceneContextctx.CompileContext.cursor: numbercursor; // 36: the third starts at 21 and ends at 36 yield* ctx: SceneContextctx.CompileContext.chain(...generators: Generator<Frame, unknown, unknown>[]): Generator<Frame, void, unknown>
Run operations consecutively, waiting for each to finish.
chain
(...const dots: Dot[]dots.Array<Dot>.map<Generator<number, void, unknown>>(callbackfn: (value: Dot, index: number, array: Dot[]) => Generator<number, void, unknown>, thisArg?: any): Generator<number, void, unknown>[]
Calls a defined callback function on each element of an array, and returns an array that contains the results.
@paramcallbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.@paramthisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
map
((d: Dotd) => d: Dotd.
LayoutActor<{}>.layout: {
    x: ActorFieldSignal<LayoutValue>;
    y: ActorFieldSignal<LayoutValue>;
    width: TypedFieldSignal<number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>, number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>>;
    height: TypedFieldSignal<number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>, number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>>;
} & SignalGroupDefinition
layout
.x: ActorFieldSignal<LayoutValue>x.LiveSignal<LayoutValue>.tween(target: LayoutValue, options: number | SignalTweenOptions<LayoutValue>): Generator<Frame, void, unknown>
Tween to target over authored seconds — must yield*
tween
(50, 0.2)));
ctx: SceneContextctx.CompileContext.cursor: numbercursor; // 54 yield* ctx: SceneContextctx.CompileContext.delay(duration: Duration, generator: Generator<Frame, unknown, unknown>): Generator<Frame, void, unknown>delay(0.5, const dots: Dot[]dots[0].
LayoutActor<{}>.layout: {
    x: ActorFieldSignal<LayoutValue>;
    y: ActorFieldSignal<LayoutValue>;
    width: TypedFieldSignal<number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>, number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>>;
    height: TypedFieldSignal<number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>, number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>>;
} & SignalGroupDefinition
layout
.x: ActorFieldSignal<LayoutValue>x.LiveSignal<LayoutValue>.tween(target: LayoutValue, options: number | SignalTweenOptions<LayoutValue>): Generator<Frame, void, unknown>
Tween to target over authored seconds — must yield*
tween
(0, 1));
ctx: SceneContextctx.CompileContext.cursor: numbercursor; // 99 }).SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>.build(args: SceneBuildInput<Readonly<Record<never, SceneInput>>>): CompiledScene<void, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>build({ viewport: SceneViewportviewport: { SceneViewport.width: numberwidth: 1920, SceneViewport.height: numberheight: 1080 }, fps: numberfps: 30 });

A branch is any generator: a tween, an actor method, another verb. Nesting them is how a scene builds a timeline.

Five bars raised with ctx.all, dropped, then raised again with ctx.sequence(0.08, …). The track beside raise shows both: five starts on one frame under all, a staircase under sequence.

Scopes

ctx.scope(fn) runs fn with a child context and moves the cursor as fn does. Actors spawned through the child despawn when it ends, so a scope suits something that exists for one beat of the scene. It returns what fn returns:

import { function createScene<TSceneExports, const I extends SceneInputs = Readonly<Record<never, SceneInput>>, const L extends SceneLayers = Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<I, L, TSceneExports>, options?: SceneDefinitionOptions<I, L>): SceneDefinition<I, L, TSceneExports>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
, class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor } from "@motionactor/core";
class class FlashFlash extends class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor {} createScene<void, Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>, options?: SceneDefinitionOptions<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>> | undefined): SceneDefinition<...>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
(function* (ctx: SceneContextctx) {
const const peak: numberpeak = yield* ctx: SceneContextctx.CompileContext.scope<number>(fn: (scope: CompileContext) => Generator<number, number, unknown>): Generator<number, number, unknown>scope(function* (beat: CompileContextbeat) { const const flash: Flashflash = beat: CompileContextbeat.
CompileContext.spawn<Flash>(definition: ActorClass<Flash>, overrides?: {
    measuredBox?: undefined;
    layout?: {
        x?: SignalInputValue<LayoutValue> | undefined;
        y?: SignalInputValue<LayoutValue> | undefined;
        width?: Readonly<{
            kind: "layout.percent";
            value: number;
        }> | SignalInputValue<number | `${number}%` | Readonly<{
            kind: "layout.percent";
            value: number;
        }>> | undefined;
        height?: Readonly<{
            kind: "layout.percent";
            value: number;
        }> | SignalInputValue<number | ... 1 more ... | Readonly<...>> | undefined;
    } | undefined;
    ... 20 more ...;
    parent?: undefined;
} | undefined, options?: {
    ...;
} | undefined): Flash (+1 overload)
spawn
(class FlashFlash);
yield* const flash: Flashflash.
LayoutActor<{}>.appearance: {
    opacity: TypedFieldSignal<number, number>;
} & SignalGroupDefinition
appearance
.opacity: TypedFieldSignal<number, number>opacity.function tween(target: number, options: number | SignalTweenOptions<number>): Generator<Frame, void, unknown>
Tween to target over authored seconds — must yield*
tween
(0, 1);
return beat: CompileContextbeat.CompileContext.cursor: numbercursor; }); const peak: numberpeak; // 30 ctx: SceneContextctx.CompileContext.cursor: numbercursor; // 30, and the flash has despawned }).SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>.build(args: SceneBuildInput<Readonly<Record<never, SceneInput>>>): CompiledScene<void, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>build({ viewport: SceneViewportviewport: { SceneViewport.width: numberwidth: 1920, SceneViewport.height: numberheight: 1080 }, fps: numberfps: 30 });

Spawning

ctx.spawn(Class, overrides, { id }) creates an actor at the cursor and returns it at once, taking no time. Overrides set its initial values and are required when the class declares a field with no default. Without an id the actor gets a generated one such as Card-2.

Give the options an enter and spawn returns a generator instead: it spawns, then runs the actor’s onEnter with enter as its params, and returns the actor when that finishes.

import { function createScene<TSceneExports, const I extends SceneInputs = Readonly<Record<never, SceneInput>>, const L extends SceneLayers = Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<I, L, TSceneExports>, options?: SceneDefinitionOptions<I, L>): SceneDefinition<I, L, TSceneExports>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
,
const Easing: {
    readonly linear: EasingFunction;
    readonly inQuad: EasingFunction;
    readonly outQuad: EasingFunction;
    readonly inOutQuad: EasingFunction;
    readonly inCubic: EasingFunction;
    readonly outCubic: EasingFunction;
    readonly inOutCubic: EasingFunction;
    readonly inQuart: EasingFunction;
    readonly outQuart: EasingFunction;
    readonly inOutQuart: EasingFunction;
    readonly bezier: (x1: number, y1: number, x2: number, y2: number) => EasingFunction;
    readonly wiggle: (count: number, type?: WiggleType, amplitude?: number) => EasingFunction;
    readonly in: (easing: EasingFunction) => EasingFunction;
    readonly out: (easing: EasingFunction) => EasingFunction;
    readonly inOut: (easing: EasingFunction) => EasingFunction;
}
Easing
, class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor } from "@motionactor/core";
class class CardCard extends class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor { *
Card.onEnter({ rise }?: {
    rise?: number;
}): Generator<number, void, unknown>
onEnter
({ rise: numberrise = 40 }: { rise?: number | undefinedrise?: number } = {}) {
this.
LayoutActor<{}>.layout: {
    x: ActorFieldSignal<LayoutValue>;
    y: ActorFieldSignal<LayoutValue>;
    width: TypedFieldSignal<number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>, number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>>;
    height: TypedFieldSignal<number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>, number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>>;
} & SignalGroupDefinition
layout
.y: ActorFieldSignal<LayoutValue>y.LiveSignal<LayoutValue>.set(value: LayoutValue): void
Set value instantly at current cursor (no yield needed)
set
(rise: numberrise);
yield* this.Actor<{}>.runtime: ActorRuntimeOrchestration<this>runtime.ActorRuntimeOrchestration<this>.all(...generators: Generator<Frame, unknown, unknown>[]): Generator<Frame, void, unknown>all( this.
LayoutActor<{}>.layout: {
    x: ActorFieldSignal<LayoutValue>;
    y: ActorFieldSignal<LayoutValue>;
    width: TypedFieldSignal<number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>, number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>>;
    height: TypedFieldSignal<number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>, number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>>;
} & SignalGroupDefinition
layout
.y: ActorFieldSignal<LayoutValue>y.LiveSignal<LayoutValue>.tween(target: LayoutValue, options: number | SignalTweenOptions<LayoutValue>): Generator<Frame, void, unknown>
Tween to target over authored seconds — must yield*
tween
(0, { SignalTweenOptions<LayoutValue>.duration: numberduration: 0.5, SignalTweenOptions<LayoutValue>.easing?: EasingFunction | undefinedeasing:
const Easing: {
    readonly linear: EasingFunction;
    readonly inQuad: EasingFunction;
    readonly outQuad: EasingFunction;
    readonly inOutQuad: EasingFunction;
    readonly inCubic: EasingFunction;
    readonly outCubic: EasingFunction;
    readonly inOutCubic: EasingFunction;
    readonly inQuart: EasingFunction;
    readonly outQuart: EasingFunction;
    readonly inOutQuart: EasingFunction;
    readonly bezier: (x1: number, y1: number, x2: number, y2: number) => EasingFunction;
    readonly wiggle: (count: number, type?: WiggleType, amplitude?: number) => EasingFunction;
    readonly in: (easing: EasingFunction) => EasingFunction;
    readonly out: (easing: EasingFunction) => EasingFunction;
    readonly inOut: (easing: EasingFunction) => EasingFunction;
}
Easing
.outCubic: EasingFunctionoutCubic }),
this.
LayoutActor<{}>.appearance: {
    opacity: TypedFieldSignal<number, number>;
} & SignalGroupDefinition
appearance
.opacity: TypedFieldSignal<number, number>opacity.function tween(target: number, options: number | SignalTweenOptions<number>): Generator<Frame, void, unknown>
Tween to target over authored seconds — must yield*
tween
(1, 0.5),
); } }
createScene<{
    card: Card;
}, Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
    card: Card;
}>, options?: SceneDefinitionOptions<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>> | undefined): SceneDefinition<...>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
(function* (ctx: SceneContextctx) {
const const card: Cardcard = yield* ctx: SceneContextctx.
CompileContext.spawn<Card, {
    rise: number;
}>(definition: ActorClass<Card>, overrides: {
    measuredBox?: undefined;
    transform?: {
        scale?: SignalInputValue<number> | undefined;
        rotation?: SignalInputValue<number> | undefined;
    } | undefined;
    id?: undefined;
    appearance?: {
        opacity?: SignalInputValue<number> | undefined;
    } | undefined;
    enter?: undefined;
    onEnter?: undefined;
    layout?: {
        x?: SignalInputValue<LayoutValue> | undefined;
        y?: SignalInputValue<LayoutValue> | undefined;
        width?: Readonly<...> | ... 1 more ... | undefined;
        height?: Readonly<...> | ... 1 more ... | undefined;
    } | undefined;
    ... 15 more ...;
    parent?: undefined;
} | undefined, options: {
    ...;
}): Generator<...> (+1 overload)
spawn
(
class CardCard, {
appearance?: {
    opacity?: SignalInputValue<number> | undefined;
} | undefined
appearance
: { opacity?: SignalInputValue<number> | undefinedopacity: 0 } },
{ id?: string | undefinedid: "card",
enter: {
    rise: number;
}
enter
: { rise: numberrise: 80 } },
); ctx: SceneContextctx.CompileContext.cursor: numbercursor; // 15 return { card: Cardcard }; }).
SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, { ...; }>.build(args: SceneBuildInput<Readonly<Record<never, SceneInput>>>): CompiledScene<{
    card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
build
({ viewport: SceneViewportviewport: { SceneViewport.width: numberwidth: 1920, SceneViewport.height: numberheight: 1080 }, fps: numberfps: 30 });

Inside an actor

An actor method is a generator too, and this.runtime gives it the same all, chain, sequence, delay and wait. They work while the actor is in a scene; called on an actor that is not, they throw “Actor instances can only coordinate generators while attached to a scene.”

The runtime adds two verbs that start work without moving anyone’s cursor:

Verb Runs Ends
fork(fn) fn once, from the cursor When fn returns
loop(fn) fn again each time it returns When the scene ends
loop({ iterations }, fn) fn that many times After the last time

Both take an optional name first. Starting a fork or a loop under a name the actor already runs replaces the old one: it starts nothing new, though a tween already under way plays to its end.

import { function createScene<TSceneExports, const I extends SceneInputs = Readonly<Record<never, SceneInput>>, const L extends SceneLayers = Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<I, L, TSceneExports>, options?: SceneDefinitionOptions<I, L>): SceneDefinition<I, L, TSceneExports>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
, class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor } from "@motionactor/core";
class class BeaconBeacon extends class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor { *Beacon.beat(): Generator<number, void, unknown>beat() { yield* this.
LayoutActor<{}>.appearance: {
    opacity: TypedFieldSignal<number, number>;
} & SignalGroupDefinition
appearance
.opacity: TypedFieldSignal<number, number>opacity.function tween(target: number, options: number | SignalTweenOptions<number>): Generator<Frame, void, unknown>
Tween to target over authored seconds — must yield*
tween
(0.4, 0.5);
yield* this.
LayoutActor<{}>.appearance: {
    opacity: TypedFieldSignal<number, number>;
} & SignalGroupDefinition
appearance
.opacity: TypedFieldSignal<number, number>opacity.function tween(target: number, options: number | SignalTweenOptions<number>): Generator<Frame, void, unknown>
Tween to target over authored seconds — must yield*
tween
(1, 0.5);
} Beacon.calm(): voidcalm() { this.Actor<{}>.runtime: ActorRuntimeOrchestration<this>runtime.ActorRuntimeOrchestration<this>.loop(name: string, generatorFactory: (this: this) => Generator<Frame, void, unknown>): void (+3 overloads)loop("pulse", function* (this: Beaconthis: class BeaconBeacon) { yield* this.Beacon.beat(): Generator<number, void, unknown>beat(); yield* this.Actor<{}>.runtime: ActorRuntimeOrchestration<Beacon>runtime.ActorRuntimeOrchestration<Beacon>.wait(duration: Duration): Generator<Frame, void, unknown>wait(1); }); } Beacon.alarm(): voidalarm() { this.Actor<{}>.runtime: ActorRuntimeOrchestration<this>runtime.ActorRuntimeOrchestration<this>.loop(name: string, generatorFactory: (this: this) => Generator<Frame, void, unknown>): void (+3 overloads)loop("pulse", function* (this: Beaconthis: class BeaconBeacon) { yield* this.Beacon.beat(): Generator<number, void, unknown>beat(); }); } } createScene<void, Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>, options?: SceneDefinitionOptions<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>> | undefined): SceneDefinition<...>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
(function* (ctx: SceneContextctx) {
const const beacon: Beaconbeacon = ctx: SceneContextctx.
CompileContext.spawn<Beacon>(definition: ActorClass<Beacon>, overrides?: {
    measuredBox?: undefined;
    transform?: {
        scale?: SignalInputValue<number> | undefined;
        rotation?: SignalInputValue<number> | undefined;
    } | undefined;
    id?: undefined;
    beat?: undefined;
    calm?: undefined;
    alarm?: undefined;
    layout?: {
        x?: SignalInputValue<LayoutValue> | undefined;
        y?: SignalInputValue<LayoutValue> | undefined;
        width?: Readonly<{
            kind: "layout.percent";
            value: number;
        }> | SignalInputValue<...> | undefined;
        height?: Readonly<...> | ... 1 more ... | undefined;
    } | undefined;
    ... 18 more ...;
    parent?: undefined;
} | undefined, options?: {
    ...;
} | undefined): Beacon (+1 overload)
spawn
(class BeaconBeacon);
const beacon: Beaconbeacon.Beacon.calm(): voidcalm(); yield* ctx: SceneContextctx.CompileContext.wait(duration: Duration): Generator<Frame, void, unknown>wait(4); const beacon: Beaconbeacon.Beacon.alarm(): voidalarm(); yield* ctx: SceneContextctx.CompileContext.wait(duration: Duration): Generator<Frame, void, unknown>wait(3); }).SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>.build(args: SceneBuildInput<Readonly<Record<never, SceneInput>>>): CompiledScene<void, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>build({ viewport: SceneViewportviewport: { SceneViewport.width: numberwidth: 1920, SceneViewport.height: numberheight: 1080 }, fps: numberfps: 30 });

A scene lasts until its script returns or until its last finite fork or loop ends, whichever is later. An endless loop never lengthens it.

Reads across branches

The script, every branch of all and sequence, every fork, loop and generator handler step together in frame order. A read sees every write made at an earlier frame, whichever branch made it. At a frame two branches share, work a branch started steps before that branch, so the script reads what its forks and handlers wrote at that frame. A tween takes its start value the same way, at the frame its branch reaches it.

import { function createScene<TSceneExports, const I extends SceneInputs = Readonly<Record<never, SceneInput>>, const L extends SceneLayers = Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<I, L, TSceneExports>, options?: SceneDefinitionOptions<I, L>): SceneDefinition<I, L, TSceneExports>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
, class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor,
function signal<T>(): RequiredSignal<T> (+1 overload)
module signal
A timeline value on an actor, and an input to every spawn of it. Without an initial value the signal is required: `ctx.spawn` must supply it.
signal
} from "@motionactor/core";
class class CounterCounter extends class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor { Counter.count: ActorFieldSignal<number>count = signal<number>(initial: number): ActorFieldSignal<number> (+1 overload)
A timeline value on an actor, and an input to every spawn of it. Without an initial value the signal is required: `ctx.spawn` must supply it.
signal
(0);
} createScene<void, Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>, options?: SceneDefinitionOptions<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>> | undefined): SceneDefinition<...>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
(function* (ctx: SceneContextctx) {
const const counter: Countercounter = ctx: SceneContextctx.
CompileContext.spawn<Counter>(definition: ActorClass<Counter>, overrides?: {
    measuredBox?: undefined;
    count?: SignalInputValue<number> | undefined;
    layout?: {
        x?: SignalInputValue<LayoutValue> | undefined;
        y?: SignalInputValue<LayoutValue> | undefined;
        width?: Readonly<{
            kind: "layout.percent";
            value: number;
        }> | SignalInputValue<number | `${number}%` | Readonly<{
            kind: "layout.percent";
            value: number;
        }>> | undefined;
        height?: Readonly<...> | ... 1 more ... | undefined;
    } | undefined;
    ... 20 more ...;
    parent?: undefined;
} | undefined, options?: {
    ...;
} | undefined): Counter (+1 overload)
spawn
(class CounterCounter);
yield* ctx: SceneContextctx.CompileContext.all(...generators: Generator<Frame, unknown, unknown>[]): Generator<Frame, void, unknown>all( (function* () { yield* ctx: SceneContextctx.CompileContext.wait(duration: Duration): Generator<Frame, void, unknown>wait(1); const counter: Countercounter.Counter.count: ActorFieldSignal<number>count.LiveSignal<number>.set(value: number): void
Set value instantly at current cursor (no yield needed)
set
(const counter: Countercounter.Counter.count: () => numbercount() + 1); // 11: the other branch wrote 10 at frame 15
})(), (function* () { yield* ctx: SceneContextctx.CompileContext.wait(duration: Duration): Generator<Frame, void, unknown>wait(0.5); const counter: Countercounter.Counter.count: ActorFieldSignal<number>count.LiveSignal<number>.set(value: number): void
Set value instantly at current cursor (no yield needed)
set
(10);
})(), ); const counter: Countercounter.Actor<{}>.runtime: ActorRuntimeOrchestration<Counter>runtime.ActorRuntimeOrchestration<Counter>.fork(generatorFactory: (this: Counter) => Generator<Frame, void, unknown>): void (+1 overload)fork(function* () { yield* const counter: Countercounter.Actor<{}>.runtime: ActorRuntimeOrchestration<Counter>runtime.ActorRuntimeOrchestration<Counter>.wait(duration: Duration): Generator<Frame, void, unknown>wait(1); const counter: Countercounter.Counter.count: ActorFieldSignal<number>count.LiveSignal<number>.set(value: number): void
Set value instantly at current cursor (no yield needed)
set
(20);
}); yield* ctx: SceneContextctx.CompileContext.wait(duration: Duration): Generator<Frame, void, unknown>wait(1); const counter: Countercounter.Counter.count: () => numbercount(); // 20: the fork wrote at frame 60 and stepped first }).SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>.build(args: SceneBuildInput<Readonly<Record<never, SceneInput>>>): CompiledScene<void, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>build({ viewport: SceneViewportviewport: { SceneViewport.width: numberwidth: 1920, SceneViewport.height: numberheight: 1080 }, fps: numberfps: 30 });

Inputs and layers

Give createScene inputs or layers to declare what a host supplies. The script receives the input values, the viewport and the layer handles after ctx, and compiles each time a host calls build:

import { function createScene<TSceneExports, const I extends SceneInputs = Readonly<Record<never, SceneInput>>, const L extends SceneLayers = Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<I, L, TSceneExports>, options?: SceneDefinitionOptions<I, L>): SceneDefinition<I, L, TSceneExports>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
,
const input: {
    of: {
        <T extends AnyType>(type: T): ValueInput<T, TypeValue<T>, true>;
        <T extends AnyType>(type: T, defaultValue: null): ValueInput<T, TypeValue<T> | null, false>;
        <T extends AnyType>(type: T, defaultValue: TypeValue<T>): ValueInput<T, TypeValue<T>, false>;
        <TInstance extends object>(blueprint: ActorClass<TInstance>): ActorInput<TInstance>;
    };
}
input
, class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor,
function signal<T>(): RequiredSignal<T> (+1 overload)
module signal
A timeline value on an actor, and an input to every spawn of it. Without an initial value the signal is required: `ctx.spawn` must supply it.
signal
} from "@motionactor/core";
import { const image: AssetType<"image", "image">image, const text: DataType<"text", ZodString>text } from "@motionactor/schema"; class class PosterPoster extends class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor { Poster.source: TypedRequiredSignal<AssetRef<"image">, AssetRef<"image">>source =
function signal<T>(): RequiredSignal<T> (+1 overload)
module signal
A timeline value on an actor, and an input to every spawn of it. Without an initial value the signal is required: `ctx.spawn` must supply it.
signal
.signal.of: <AssetType<"image", "image">>(type: AssetType<"image", "image">) => TypedRequiredSignal<AssetRef<"image">, AssetRef<"image">> (+2 overloads)
A signal whose value is a named type from `@motionactor/schema`. The type survives to reflection (`field.type`) and spawn checks values against its schema. `signal.of(audio)` is required; `signal.of(image, null)` is not. A decoding type (`typeDecodes`: `colorType`) also maps what is written: the default, spawn inputs, and `set`/`tween`/`play` values go through its schema, so the field only ever holds the type's value.
of
(const image: AssetType<"image", "image">image);
Poster.caption: ActorFieldSignal<string>caption = signal<string>(initial: string): ActorFieldSignal<string> (+1 overload)
A timeline value on an actor, and an input to every spawn of it. Without an initial value the signal is required: `ctx.spawn` must supply it.
signal
("");
} export const
const poster: SceneDefinition<{
    readonly hero: ValueInput<AssetType<"image", "image">, AssetRef<"image">, true>;
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
    card: Poster;
}>
poster
=
createScene<{
    card: Poster;
}, {
    readonly hero: ValueInput<AssetType<"image", "image">, AssetRef<"image">, true>;
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<{
    readonly hero: ValueInput<AssetType<"image", "image">, AssetRef<"image">, true>;
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
}, Readonly<...>, {
    card: Poster;
}>, options?: SceneDefinitionOptions<...> | undefined): SceneDefinition<...>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
(
function* (ctx: SceneContextctx, { hero: AssetRef<"image">hero, title: stringtitle }, { width: numberwidth }) { const const card: Postercard = ctx: SceneContextctx.
CompileContext.spawn<Poster>(definition: ActorClass<Poster>, overrides: {
    source: SignalInputValue<AssetRef<"image">>;
    transform?: {
        scale?: SignalInputValue<number> | undefined;
        rotation?: SignalInputValue<number> | undefined;
    } | undefined;
    measuredBox?: undefined;
    id?: undefined;
    caption?: SignalInputValue<string> | undefined;
    layout?: {
        width?: Readonly<{
            kind: "layout.percent";
            value: number;
        }> | SignalInputValue<number | `${number}%` | Readonly<{
            kind: "layout.percent";
            value: number;
        }>> | undefined;
        x?: SignalInputValue<...> | undefined;
        y?: SignalInputValue<...> | undefined;
        height?: Readonly<...> | ... 1 more ... | undefined;
    } | undefined;
    ... 18 more ...;
    parent?: undefined;
}, options?: {
    ...;
} | undefined): Poster (+1 overload)
spawn
(class PosterPoster, {
source: SignalInputValue<AssetRef<"image">>source: hero: AssetRef<"image">hero, caption?: SignalInputValue<string> | undefinedcaption: title: stringtitle,
layout?: {
    width?: Readonly<{
        kind: "layout.percent";
        value: number;
    }> | SignalInputValue<number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>> | undefined;
    x?: SignalInputValue<LayoutValue> | undefined;
    y?: SignalInputValue<LayoutValue> | undefined;
    height?: Readonly<{
        kind: "layout.percent";
        value: number;
    }> | SignalInputValue<number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>> | undefined;
} | undefined
layout
: { x?: SignalInputValue<LayoutValue> | undefinedx: width: numberwidth * 0.1 },
}); yield* ctx: SceneContextctx.CompileContext.wait(duration: Duration): Generator<Frame, void, unknown>wait(2); return { card: Postercard }; }, {
SceneDefinitionOptions<{ readonly hero: ValueInput<AssetType<"image", "image">, AssetRef<"image">, true>; readonly title: ValueInput<DataType<"text", ZodString>, string, false>; }, Readonly<...>>.inputs?: {
    readonly hero: ValueInput<AssetType<"image", "image">, AssetRef<"image">, true>;
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
} | undefined
inputs
: {
hero: ValueInput<AssetType<"image", "image">, AssetRef<"image">, true>hero:
const input: {
    of: {
        <T extends AnyType>(type: T): ValueInput<T, TypeValue<T>, true>;
        <T extends AnyType>(type: T, defaultValue: null): ValueInput<T, TypeValue<T> | null, false>;
        <T extends AnyType>(type: T, defaultValue: TypeValue<T>): ValueInput<T, TypeValue<T>, false>;
        <TInstance extends object>(blueprint: ActorClass<TInstance>): ActorInput<TInstance>;
    };
}
input
.of: <AssetType<"image", "image">>(type: AssetType<"image", "image">) => ValueInput<AssetType<"image", "image">, AssetRef<"image">, true> (+3 overloads)
Declare a scene input. Over a named type from `@motionactor/schema`, `input.of(image)` is required; `input.of(text, "Launch day")` and `input.of(image, null)` are not. Over an actor blueprint, `input.of(Narrator)` takes a `Narrator` from the scene that stages it, and has no default.
of
(const image: AssetType<"image", "image">image),
title: ValueInput<DataType<"text", ZodString>, string, false>title:
const input: {
    of: {
        <T extends AnyType>(type: T): ValueInput<T, TypeValue<T>, true>;
        <T extends AnyType>(type: T, defaultValue: null): ValueInput<T, TypeValue<T> | null, false>;
        <T extends AnyType>(type: T, defaultValue: TypeValue<T>): ValueInput<T, TypeValue<T>, false>;
        <TInstance extends object>(blueprint: ActorClass<TInstance>): ActorInput<TInstance>;
    };
}
input
.of: <DataType<"text", ZodString>>(type: DataType<"text", ZodString>, defaultValue: string) => ValueInput<DataType<"text", ZodString>, string, false> (+3 overloads)
Declare a scene input. Over a named type from `@motionactor/schema`, `input.of(image)` is required; `input.of(text, "Launch day")` and `input.of(image, null)` are not. Over an actor blueprint, `input.of(Narrator)` takes a `Narrator` from the scene that stages it, and has no default.
of
(const text: DataType<"text", ZodString>text, "Launch day"),
}, }, ); const
const compiled: CompiledScene<{
    card: Poster;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled
=
const poster: SceneDefinition<{
    readonly hero: ValueInput<AssetType<"image", "image">, AssetRef<"image">, true>;
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
    card: Poster;
}>
poster
.
SceneDefinition<{ readonly hero: ValueInput<AssetType<"image", "image">, AssetRef<"image">, true>; readonly title: ValueInput<DataType<"text", ZodString>, string, false>; }, Readonly<...>, { ...; }>.build(args: SceneBuildInput<{
    readonly hero: ValueInput<AssetType<"image", "image">, AssetRef<"image">, true>;
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
}>): CompiledScene<{
    card: Poster;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
build
({
viewport: SceneViewportviewport: { SceneViewport.width: numberwidth: 1920, SceneViewport.height: numberheight: 1080 },
inputs: HostInputArgs<{
    readonly hero: ValueInput<AssetType<"image", "image">, AssetRef<"image">, true>;
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
}>
inputs
: { hero: AssetRef<"image">hero: const image: AssetType<"image", "image">image.
AssetType<"image", "image">.file(src: string, options?: {
    mimeType?: string;
}): AssetRef<"image">
file
("hero.jpg") },
fps: numberfps: 30, });
const compiled: CompiledScene<{
    card: Poster;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled
.
CompiledScene<{ ...; }, CompiledLayersOf<...>>.at(frame: Frame, options?: FrameResolutionOptions): FrameState<{
    card: Poster;
}>
at
(0).
FrameState<{ ...; }>.scene: {
    card: ResolvedActorOfInstance<Poster> | undefined;
}
scene
.card: ResolvedActorOfInstance<Poster> | undefinedcard?.
layout: {
    width: number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>;
    x: LayoutValue;
    y: LayoutValue;
    height: number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>;
}
layout
.x: LayoutValue | undefinedx; // 192

input.of(type) is required and input.of(type, fallback) is not, as with signal.of. build checks each value against its type and names the input that is missing or wrong. An input is a plain value: a different one is a new build. reflectSceneInputs(poster) lists the declared inputs for an editor. Layers are on Backends and layers.

Staging scenes

Every scene’s ctx has stage, which runs a scene as one part of a longer timeline. stage takes a script, or a definition with its inputs, and returns the scene’s exports:

import {
  function createScene<TSceneExports, const I extends SceneInputs = Readonly<Record<never, SceneInput>>, const L extends SceneLayers = Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<I, L, TSceneExports>, options?: SceneDefinitionOptions<I, L>): SceneDefinition<I, L, TSceneExports>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
,
const input: {
    of: {
        <T extends AnyType>(type: T): ValueInput<T, TypeValue<T>, true>;
        <T extends AnyType>(type: T, defaultValue: null): ValueInput<T, TypeValue<T> | null, false>;
        <T extends AnyType>(type: T, defaultValue: TypeValue<T>): ValueInput<T, TypeValue<T>, false>;
        <TInstance extends object>(blueprint: ActorClass<TInstance>): ActorInput<TInstance>;
    };
}
input
,
class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor,
function signal<T>(): RequiredSignal<T> (+1 overload)
module signal
A timeline value on an actor, and an input to every spawn of it. Without an initial value the signal is required: `ctx.spawn` must supply it.
signal
,
} from "@motionactor/core"; import { const text: DataType<"text", ZodString>text } from "@motionactor/schema"; class class TitleTitle extends class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor { Title.text: ActorFieldSignal<string>text = signal<string>(initial: string): ActorFieldSignal<string> (+1 overload)
A timeline value on an actor, and an input to every spawn of it. Without an initial value the signal is required: `ctx.spawn` must supply it.
signal
("");
} const
const intro: SceneDefinition<{
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>
intro
=
createScene<void, {
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<{
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>, options?: SceneDefinitionOptions<{
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
}, Readonly<...>> | undefined): SceneDefinition<...>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
(
function* (ctx: SceneContextctx, { title: stringtitle }) { ctx: SceneContextctx.
CompileContext.spawn<Title>(definition: ActorClass<Title>, overrides?: {
    text?: SignalInputValue<string> | undefined;
    transform?: {
        scale?: SignalInputValue<number> | undefined;
        rotation?: SignalInputValue<number> | undefined;
    } | undefined;
    measuredBox?: undefined;
    id?: undefined;
    layout?: {
        x?: SignalInputValue<LayoutValue> | undefined;
        y?: SignalInputValue<LayoutValue> | undefined;
        width?: Readonly<{
            kind: "layout.percent";
            value: number;
        }> | SignalInputValue<...> | undefined;
        height?: Readonly<...> | ... 1 more ... | undefined;
    } | undefined;
    ... 18 more ...;
    parent?: undefined;
} | undefined, options?: {
    ...;
} | undefined): Title (+1 overload)
spawn
(class TitleTitle, { text?: SignalInputValue<string> | undefinedtext: title: stringtitle });
yield* ctx: SceneContextctx.CompileContext.wait(duration: Duration): Generator<Frame, void, unknown>wait(1); }, {
SceneDefinitionOptions<{ readonly title: ValueInput<DataType<"text", ZodString>, string, false>; }, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>.inputs?: {
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
} | undefined
inputs
: { title: ValueInput<DataType<"text", ZodString>, string, false>title:
const input: {
    of: {
        <T extends AnyType>(type: T): ValueInput<T, TypeValue<T>, true>;
        <T extends AnyType>(type: T, defaultValue: null): ValueInput<T, TypeValue<T> | null, false>;
        <T extends AnyType>(type: T, defaultValue: TypeValue<T>): ValueInput<T, TypeValue<T>, false>;
        <TInstance extends object>(blueprint: ActorClass<TInstance>): ActorInput<TInstance>;
    };
}
input
.of: <DataType<"text", ZodString>>(type: DataType<"text", ZodString>, defaultValue: string) => ValueInput<DataType<"text", ZodString>, string, false> (+3 overloads)
Declare a scene input. Over a named type from `@motionactor/schema`, `input.of(image)` is required; `input.of(text, "Launch day")` and `input.of(image, null)` are not. Over an actor blueprint, `input.of(Narrator)` takes a `Narrator` from the scene that stages it, and has no default.
of
(const text: DataType<"text", ZodString>text, "Hi") } },
); const const film: CompiledScene<void, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>film = createScene<void, Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>, options?: SceneDefinitionOptions<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>> | undefined): SceneDefinition<...>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
(function* (ctx: SceneContextctx) {
yield* ctx: SceneContextctx.
SceneContext.stage<{
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>(definition: SceneDefinition<{
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>, options?: (StageOptions & {
    inputs?: SceneInputArgs<...> | undefined;
}) | undefined): Generator<...> (+2 overloads)
Stage a `createScene` definition with its inputs, under the build's viewport and on its clock. Its declared layers are in force while it is on stage, below this scene's. It may stage scenes in turn.
stage
(
const intro: SceneDefinition<{
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>
intro
, { StageOptions.id?: string | undefinedid: "intro",
inputs?: SceneInputArgs<{
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
}> | undefined
inputs
: { title?: string | undefinedtitle: "Hello" } });
yield* ctx: SceneContextctx.SceneContext.stage<void>(sceneFn: (ctx: SceneContext) => Generator<number, void, unknown>, options?: StageOptions): Generator<number, void, unknown> (+2 overloads)stage( function* (scene: SceneContextscene) { yield* scene: SceneContextscene.CompileContext.wait(duration: Duration): Generator<Frame, void, unknown>wait(2); }, { StageOptions.id?: string | undefinedid: "outro" }, ); }).SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>.build(args: SceneBuildInput<Readonly<Record<never, SceneInput>>>): CompiledScene<void, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>build({ viewport: SceneViewportviewport: { SceneViewport.width: numberwidth: 1920, SceneViewport.height: numberheight: 1080 }, fps: numberfps: 30 }); const film: CompiledScene<void, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>film.CompiledScene<TSceneExports = void | SceneExportShape, TLayers extends CompiledLayers = Readonly<Record<string, CompiledLayer<BackendRef<string>>>>>.totalDuration: numbertotalDuration; // 90 const film: CompiledScene<void, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>film.CompiledScene<TSceneExports = void | SceneExportShape, TLayers extends CompiledLayers = Readonly<Record<string, CompiledLayer<BackendRef<string>>>>>.placements: readonly ScenePlacement[]
Every scene staged in the build, at any depth, in start order: a stager before what it stages.
placements
.ReadonlyArray<ScenePlacement>.map<(string | number)[]>(callbackfn: (value: ScenePlacement, index: number, array: readonly ScenePlacement[]) => (string | number)[], thisArg?: any): (string | number)[][]
Calls a defined callback function on each element of an array, and returns an array that contains the results.
@paramcallbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.@paramthisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
map
((p: ScenePlacementp) => [p: ScenePlacementp.ScenePlacement.sceneId: string
Its `ctx.stage` id: given, or minted.
sceneId
, p: ScenePlacementp.ScenePlacement.startFrame: number
On the build's timeline, at any depth.
startFrame
, p: ScenePlacementp.ScenePlacement.endFrame: numberendFrame]);
// [["intro", 0, 30], ["outro", 30, 90]]

Each stage runs in its stager’s scope, so its actors leave when it ends. Actors spawned on the stager’s own ctx stay for its whole timeline, which is where a music bed goes. Two scenes one stager stages cannot overlap: staging two under ctx.all throws, naming both and their frames.

A staged definition runs on the built scene’s clock, and its length in seconds holds.

A staged scene stages in turn. placements lists every staged scene at any depth, stager first: each has a path ("a/leaf"), its stager’s parentPath (null at the top) and its depth (1 at the top). A definition already on stage above itself throws, and so does a second stage with one path; give each stage under one stager its own id.

const const leaf: SceneDefinition<{}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>leaf = createScene<void, {}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<{}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>, options?: SceneDefinitionOptions<{}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>> | undefined): SceneDefinition<{}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
(function* (ctx: SceneContextctx) {
yield* ctx: SceneContextctx.CompileContext.wait(duration: Duration): Generator<Frame, void, unknown>wait(1); }, { SceneDefinitionOptions<{}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>.inputs?: {} | undefinedinputs: {} }); const const chapter: SceneDefinition<{}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>chapter = createScene<void, {}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<{}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>, options?: SceneDefinitionOptions<{}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>> | undefined): SceneDefinition<{}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
(function* (ctx: SceneContextctx) {
yield* ctx: SceneContextctx.CompileContext.wait(duration: Duration): Generator<Frame, void, unknown>wait(0.5); yield* ctx: SceneContextctx.
SceneContext.stage<{}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>(definition: SceneDefinition<{}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>, options?: (StageOptions & {
    inputs?: SceneInputArgs<{}> | undefined;
}) | undefined): Generator<number, void, unknown> (+2 overloads)
Stage a `createScene` definition with its inputs, under the build's viewport and on its clock. Its declared layers are in force while it is on stage, below this scene's. It may stage scenes in turn.
stage
(const leaf: SceneDefinition<{}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>leaf, { StageOptions.id?: string | undefinedid: "leaf" });
}, { SceneDefinitionOptions<{}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>.inputs?: {} | undefinedinputs: {} }); const const book: CompiledScene<void, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>book = createScene<void, Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>, options?: SceneDefinitionOptions<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>> | undefined): SceneDefinition<...>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
(function* (ctx: SceneContextctx) {
yield* ctx: SceneContextctx.
SceneContext.stage<{}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>(definition: SceneDefinition<{}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>, options?: (StageOptions & {
    inputs?: SceneInputArgs<{}> | undefined;
}) | undefined): Generator<number, void, unknown> (+2 overloads)
Stage a `createScene` definition with its inputs, under the build's viewport and on its clock. Its declared layers are in force while it is on stage, below this scene's. It may stage scenes in turn.
stage
(const chapter: SceneDefinition<{}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>chapter, { StageOptions.id?: string | undefinedid: "a" });
yield* ctx: SceneContextctx.
SceneContext.stage<{}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>(definition: SceneDefinition<{}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>, options?: (StageOptions & {
    inputs?: SceneInputArgs<{}> | undefined;
}) | undefined): Generator<number, void, unknown> (+2 overloads)
Stage a `createScene` definition with its inputs, under the build's viewport and on its clock. Its declared layers are in force while it is on stage, below this scene's. It may stage scenes in turn.
stage
(const chapter: SceneDefinition<{}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>chapter, { StageOptions.id?: string | undefinedid: "b" });
}).SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>.build(args: SceneBuildInput<Readonly<Record<never, SceneInput>>>): CompiledScene<void, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>build({ viewport: SceneViewportviewport: { SceneViewport.width: numberwidth: 1920, SceneViewport.height: numberheight: 1080 }, fps: numberfps: 30 }); const book: CompiledScene<void, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>book.CompiledScene<TSceneExports = void | SceneExportShape, TLayers extends CompiledLayers = Readonly<Record<string, CompiledLayer<BackendRef<string>>>>>.placements: readonly ScenePlacement[]
Every scene staged in the build, at any depth, in start order: a stager before what it stages.
placements
.ReadonlyArray<ScenePlacement>.map<(string | number)[]>(callbackfn: (value: ScenePlacement, index: number, array: readonly ScenePlacement[]) => (string | number)[], thisArg?: any): (string | number)[][]
Calls a defined callback function on each element of an array, and returns an array that contains the results.
@paramcallbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.@paramthisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
map
((p: ScenePlacementp) => [p: ScenePlacementp.ScenePlacement.path: string
Its stagers' ids and its own, joined by "/": `a/leaf`. Unique in the build.
path
, p: ScenePlacementp.ScenePlacement.depth: number
1 when the built scene staged it, 2 for a scene that one staged.
depth
, p: ScenePlacementp.ScenePlacement.startFrame: number
On the build's timeline, at any depth.
startFrame
, p: ScenePlacementp.ScenePlacement.endFrame: numberendFrame]);
// [["a", 1, 0, 45], ["a/leaf", 2, 15, 45], ["b", 1, 45, 90], ["b/leaf", 2, 60, 90]]

The host can pass the scenes to stage as well: a definition’s build takes scenes, and its script reads them from ctx.scenes. Only the built scene lists them; a staged scene’s ctx.scenes is empty.

const 
const video: SceneDefinition<{
    readonly music: ValueInput<AssetType<"audio", "audio">, AssetRef<"audio">, true>;
}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>
video
=
createScene<void, {
    readonly music: ValueInput<AssetType<"audio", "audio">, AssetRef<"audio">, true>;
}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<{
    readonly music: ValueInput<AssetType<"audio", "audio">, AssetRef<"audio">, true>;
}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>, options?: SceneDefinitionOptions<{
    readonly music: ValueInput<AssetType<"audio", "audio">, AssetRef<"audio">, true>;
}, Readonly<...>> | undefined): SceneDefinition<...>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
(
function* (ctx: SceneContextctx, { music: AssetRef<"audio">music }) { const const bed: Audiobed = ctx: SceneContextctx.
CompileContext.spawn<Audio>(definition: ActorClass<Audio>, overrides: {
    content: {
        source: SignalInputValue<AssetRef<"audio">>;
    };
    volume?: SignalInputValue<number> | undefined;
    fade?: {
        in?: SignalInputValue<number> | undefined;
        out?: SignalInputValue<number> | undefined;
    } | undefined;
    timing?: {
        start?: SignalInputValue<number> | undefined;
        end?: SignalInputValue<number> | undefined;
    } | undefined;
    sourceTime?: SignalInputValue<number> | undefined;
    ... 18 more ...;
    parent?: undefined;
}, options?: {
    ...;
} | undefined): Audio (+1 overload)
spawn
(class AudioAudio, {
content: {
    source: SignalInputValue<AssetRef<"audio">>;
}
content
: { source: SignalInputValue<AssetRef<"audio">>source: music: AssetRef<"audio">music },
volume?: SignalInputValue<number> | undefinedvolume: 0.5,
fade?: {
    in?: SignalInputValue<number> | undefined;
    out?: SignalInputValue<number> | undefined;
} | undefined
fade
: { in?: SignalInputValue<number> | undefinedin: 2, out?: SignalInputValue<number> | undefinedout: 3 },
}); const bed: Audiobed.Audio.loop(params?: MediaLoopParams): voidloop(); for (const const scene: StagedScenescene of ctx: SceneContextctx.SceneContext.scenes: readonly StagedScene[]
The scenes the host passed to `build`, in order. Empty for a staged scene.
scenes
) yield* ctx: SceneContextctx.SceneContext.stage(scene: StagedScene): Generator<Frame, unknown, unknown> (+2 overloads)stage(const scene: StagedScenescene);
}, {
SceneDefinitionOptions<{ readonly music: ValueInput<AssetType<"audio", "audio">, AssetRef<"audio">, true>; }, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>.inputs?: {
    readonly music: ValueInput<AssetType<"audio", "audio">, AssetRef<"audio">, true>;
} | undefined
inputs
: { music: ValueInput<AssetType<"audio", "audio">, AssetRef<"audio">, true>music:
const input: {
    of: {
        <T extends AnyType>(type: T): ValueInput<T, TypeValue<T>, true>;
        <T extends AnyType>(type: T, defaultValue: null): ValueInput<T, TypeValue<T> | null, false>;
        <T extends AnyType>(type: T, defaultValue: TypeValue<T>): ValueInput<T, TypeValue<T>, false>;
        <TInstance extends object>(blueprint: ActorClass<TInstance>): ActorInput<TInstance>;
    };
}
input
.of: <AssetType<"audio", "audio">>(type: AssetType<"audio", "audio">) => ValueInput<AssetType<"audio", "audio">, AssetRef<"audio">, true> (+3 overloads)
Declare a scene input. Over a named type from `@motionactor/schema`, `input.of(image)` is required; `input.of(text, "Launch day")` and `input.of(image, null)` are not. Over an actor blueprint, `input.of(Narrator)` takes a `Narrator` from the scene that stages it, and has no default.
of
(const audio: AssetType<"audio", "audio">audio) } },
);
const video: SceneDefinition<{
    readonly music: ValueInput<AssetType<"audio", "audio">, AssetRef<"audio">, true>;
}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>
video
.
SceneDefinition<{ readonly music: ValueInput<AssetType<"audio", "audio">, AssetRef<"audio">, true>; }, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>.build(args: SceneBuildInput<{
    readonly music: ValueInput<AssetType<"audio", "audio">, AssetRef<"audio">, true>;
}>): CompiledScene<void, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
build
({
viewport: SceneViewportviewport: { SceneViewport.width: numberwidth: 1920, SceneViewport.height: numberheight: 1080 },
inputs: HostInputArgs<{
    readonly music: ValueInput<AssetType<"audio", "audio">, AssetRef<"audio">, true>;
}>
inputs
: { music: AssetRef<"audio">music: const theme: AssetRef<"audio">theme },
scenes?: readonly StagedScene[] | undefined
What the built scene's `ctx.scenes` lists, in order: definitions the host picked, typically from JSON props.
scenes
: [
{ StagedScene.id: stringid: "intro", StagedScene.definition: AnySceneDefinitiondefinition: const intro: SceneDefinition<{}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>intro }, { StagedScene.id: stringid: "outro", StagedScene.definition: AnySceneDefinitiondefinition: const outro: SceneDefinition<{}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>outro }, ], fps: numberfps: 30, });

Actor inputs

input.of(Blueprint) declares an input that is an actor. The scene that stages the definition passes one of its own actors, and the script receives that actor: its fields stay live, so whatever the stager does to it reaches the staged scene, across cuts.

import {
  
function channel<TValue>(options: {
    readonly default: TValue;
    readonly record?: boolean;
    readonly equals?: (previous: TValue, next: TValue) => boolean;
}): ChannelDefinition<TValue> & {
    readonly hasDefault: true;
    readonly default: TValue;
} (+1 overload)
A channel is a writable, scope-aware cell carrying an actor-channel brand. Declared as an actor field it lives in the spawning build's store (keyed `${actorId}:${name}` by the spawn walk, like a signal); declared free (in `buildScene`, a script, or module top) it keys by its scope, in its build's store or the module's, and is shared by reference. Either way resolution reads it live. The static return type stays `ChannelDefinition<TValue>` so the `isChannelDefinition` predicate is unchanged; at runtime the handle is a live cell.
channel
,
function createScene<TSceneExports, const I extends SceneInputs = Readonly<Record<never, SceneInput>>, const L extends SceneLayers = Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<I, L, TSceneExports>, options?: SceneDefinitionOptions<I, L>): SceneDefinition<I, L, TSceneExports>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
,
function derived<TThis, T>(fn: (this: TThis) => T): DerivedDefinition<T, TThis>derived,
const input: {
    of: {
        <T extends AnyType>(type: T): ValueInput<T, TypeValue<T>, true>;
        <T extends AnyType>(type: T, defaultValue: null): ValueInput<T, TypeValue<T> | null, false>;
        <T extends AnyType>(type: T, defaultValue: TypeValue<T>): ValueInput<T, TypeValue<T>, false>;
        <TInstance extends object>(blueprint: ActorClass<TInstance>): ActorInput<TInstance>;
    };
}
input
,
class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor,
function signal<T>(): RequiredSignal<T> (+1 overload)
module signal
A timeline value on an actor, and an input to every spawn of it. Without an initial value the signal is required: `ctx.spawn` must supply it.
signal
,
} from "@motionactor/core"; import { const text: DataType<"text", ZodString>text } from "@motionactor/schema"; class class NarratorNarrator extends class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor { Narrator.volume: ActorFieldSignal<number>volume = signal<number>(initial: number): ActorFieldSignal<number> (+1 overload)
A timeline value on an actor, and an input to every spawn of it. Without an initial value the signal is required: `ctx.spawn` must supply it.
signal
(1);
Narrator.loudness: ChannelDefinition<number> & {
    readonly hasDefault: true;
    readonly default: number;
}
loudness
=
channel<number>(options: {
    readonly default: number;
    readonly record?: boolean;
    readonly equals?: ((previous: number, next: number) => boolean) | undefined;
}): ChannelDefinition<number> & {
    readonly hasDefault: true;
    readonly default: number;
} (+1 overload)
A channel is a writable, scope-aware cell carrying an actor-channel brand. Declared as an actor field it lives in the spawning build's store (keyed `${actorId}:${name}` by the spawn walk, like a signal); declared free (in `buildScene`, a script, or module top) it keys by its scope, in its build's store or the module's, and is shared by reference. Either way resolution reads it live. The static return type stays `ChannelDefinition<TValue>` so the `isChannelDefinition` predicate is unchanged; at runtime the handle is a live cell.
channel
({ default: numberdefault: 0 });
} class class MeterMeter extends class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor { Meter.level: ActorFieldSignal<number>level = signal<number>(initial: number): ActorFieldSignal<number> (+1 overload)
A timeline value on an actor, and an input to every spawn of it. Without an initial value the signal is required: `ctx.spawn` must supply it.
signal
(0);
} const
const outro: SceneDefinition<{
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
    readonly narrator: ActorInput<Narrator>;
}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
    title: string;
}>
outro
=
createScene<{
    title: string;
}, {
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
    readonly narrator: ActorInput<Narrator>;
}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<{
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
    readonly narrator: ActorInput<Narrator>;
}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
    title: string;
}>, options?: SceneDefinitionOptions<...> | undefined): SceneDefinition<...>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
(
function* (ctx: SceneContextctx, { title: stringtitle, narrator: Narratornarrator }) { ctx: SceneContextctx.
CompileContext.spawn<Meter>(definition: ActorClass<Meter>, overrides?: {
    transform?: {
        scale?: SignalInputValue<number> | undefined;
        rotation?: SignalInputValue<number> | undefined;
    } | undefined;
    measuredBox?: undefined;
    id?: undefined;
    level?: SignalInputValue<number> | undefined;
    layout?: {
        x?: SignalInputValue<LayoutValue> | undefined;
        y?: SignalInputValue<LayoutValue> | undefined;
        width?: Readonly<{
            kind: "layout.percent";
            value: number;
        }> | SignalInputValue<...> | undefined;
        height?: Readonly<...> | ... 1 more ... | undefined;
    } | undefined;
    ... 18 more ...;
    parent?: undefined;
} | undefined, options?: {
    ...;
} | undefined): Meter (+1 overload)
spawn
(class MeterMeter, { level?: SignalInputValue<number> | undefinedlevel: derived<any, number>(fn: (this: any) => number): DerivedDefinition<number, any>derived(() => narrator: Narratornarrator.Narrator.loudness: () => numberloudness() * 2) });
ctx: SceneContextctx.
CompileContext.spawn<Meter>(definition: ActorClass<Meter>, overrides?: {
    transform?: {
        scale?: SignalInputValue<number> | undefined;
        rotation?: SignalInputValue<number> | undefined;
    } | undefined;
    measuredBox?: undefined;
    id?: undefined;
    level?: SignalInputValue<number> | undefined;
    layout?: {
        x?: SignalInputValue<LayoutValue> | undefined;
        y?: SignalInputValue<LayoutValue> | undefined;
        width?: Readonly<{
            kind: "layout.percent";
            value: number;
        }> | SignalInputValue<...> | undefined;
        height?: Readonly<...> | ... 1 more ... | undefined;
    } | undefined;
    ... 18 more ...;
    parent?: undefined;
} | undefined, options?: {
    ...;
} | undefined): Meter (+1 overload)
spawn
(class MeterMeter, { level?: SignalInputValue<number> | undefinedlevel: narrator: Narratornarrator.Narrator.volume: ActorFieldSignal<number>volume });
yield* ctx: SceneContextctx.CompileContext.wait(duration: Duration): Generator<Frame, void, unknown>wait(1); return { title: stringtitle }; }, {
SceneDefinitionOptions<{ readonly title: ValueInput<DataType<"text", ZodString>, string, false>; readonly narrator: ActorInput<Narrator>; }, Readonly<...>>.inputs?: {
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
    readonly narrator: ActorInput<Narrator>;
} | undefined
inputs
: { title: ValueInput<DataType<"text", ZodString>, string, false>title:
const input: {
    of: {
        <T extends AnyType>(type: T): ValueInput<T, TypeValue<T>, true>;
        <T extends AnyType>(type: T, defaultValue: null): ValueInput<T, TypeValue<T> | null, false>;
        <T extends AnyType>(type: T, defaultValue: TypeValue<T>): ValueInput<T, TypeValue<T>, false>;
        <TInstance extends object>(blueprint: ActorClass<TInstance>): ActorInput<TInstance>;
    };
}
input
.of: <DataType<"text", ZodString>>(type: DataType<"text", ZodString>, defaultValue: string) => ValueInput<DataType<"text", ZodString>, string, false> (+3 overloads)
Declare a scene input. Over a named type from `@motionactor/schema`, `input.of(image)` is required; `input.of(text, "Launch day")` and `input.of(image, null)` are not. Over an actor blueprint, `input.of(Narrator)` takes a `Narrator` from the scene that stages it, and has no default.
of
(const text: DataType<"text", ZodString>text, "Outro"), narrator: ActorInput<Narrator>narrator:
const input: {
    of: {
        <T extends AnyType>(type: T): ValueInput<T, TypeValue<T>, true>;
        <T extends AnyType>(type: T, defaultValue: null): ValueInput<T, TypeValue<T> | null, false>;
        <T extends AnyType>(type: T, defaultValue: TypeValue<T>): ValueInput<T, TypeValue<T>, false>;
        <TInstance extends object>(blueprint: ActorClass<TInstance>): ActorInput<TInstance>;
    };
}
input
.of: <Narrator>(blueprint: ActorClass<Narrator>) => ActorInput<Narrator> (+3 overloads)
Declare a scene input. Over a named type from `@motionactor/schema`, `input.of(image)` is required; `input.of(text, "Launch day")` and `input.of(image, null)` are not. Over an actor blueprint, `input.of(Narrator)` takes a `Narrator` from the scene that stages it, and has no default.
of
(class NarratorNarrator) } },
); const const video: CompiledScene<void, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>video = createScene<void, Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>, options?: SceneDefinitionOptions<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>> | undefined): SceneDefinition<...>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
(function* (ctx: SceneContextctx) {
const const narrator: Narratornarrator = ctx: SceneContextctx.
CompileContext.spawn<Narrator>(definition: ActorClass<Narrator>, overrides?: {
    transform?: {
        scale?: SignalInputValue<number> | undefined;
        rotation?: SignalInputValue<number> | undefined;
    } | undefined;
    measuredBox?: undefined;
    id?: undefined;
    layout?: {
        x?: SignalInputValue<LayoutValue> | undefined;
        y?: SignalInputValue<LayoutValue> | undefined;
        width?: Readonly<{
            kind: "layout.percent";
            value: number;
        }> | SignalInputValue<number | `${number}%` | Readonly<...>> | undefined;
        height?: Readonly<...> | ... 1 more ... | undefined;
    } | undefined;
    ... 20 more ...;
    loudness?: undefined;
} | undefined, options?: {
    ...;
} | undefined): Narrator (+1 overload)
spawn
(class NarratorNarrator);
const narrator: Narratornarrator.Narrator.volume: ActorFieldSignal<number>volume.LiveSignal<number>.animate(name: string, options: SignalAnimationOptions<number>): void (+1 overload)
Start named branch-owned ambient signal animation.
animate
("fade", { SignalPlaybackOptions<number>.to: numberto: 0, SignalTweenOptions<T>.duration: numberduration: 2 });
yield* ctx: SceneContextctx.
SceneContext.stage<{
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
    readonly narrator: ActorInput<Narrator>;
}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
    title: string;
}>(definition: SceneDefinition<{
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
    readonly narrator: ActorInput<Narrator>;
}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
    title: string;
}>, options?: (StageOptions & {
    ...;
}) | undefined): Generator<...> (+2 overloads)
Stage a `createScene` definition with its inputs, under the build's viewport and on its clock. Its declared layers are in force while it is on stage, below this scene's. It may stage scenes in turn.
stage
(
const outro: SceneDefinition<{
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
    readonly narrator: ActorInput<Narrator>;
}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
    title: string;
}>
outro
, {
inputs?: SceneInputArgs<{
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
    readonly narrator: ActorInput<Narrator>;
}> | undefined
inputs
: { narrator: Narratornarrator, title?: string | undefinedtitle: "One" } });
yield* ctx: SceneContextctx.
SceneContext.stage<{
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
    readonly narrator: ActorInput<Narrator>;
}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
    title: string;
}>(definition: SceneDefinition<{
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
    readonly narrator: ActorInput<Narrator>;
}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
    title: string;
}>, options?: (StageOptions & {
    ...;
}) | undefined): Generator<...> (+2 overloads)
Stage a `createScene` definition with its inputs, under the build's viewport and on its clock. Its declared layers are in force while it is on stage, below this scene's. It may stage scenes in turn.
stage
(
const outro: SceneDefinition<{
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
    readonly narrator: ActorInput<Narrator>;
}, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
    title: string;
}>
outro
, {
inputs?: SceneInputArgs<{
    readonly title: ValueInput<DataType<"text", ZodString>, string, false>;
    readonly narrator: ActorInput<Narrator>;
}> | undefined
inputs
: { narrator: Narratornarrator, title?: string | undefinedtitle: "Two" } });
}).SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, void>.build(args: SceneBuildInput<Readonly<Record<never, SceneInput>>>): CompiledScene<void, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>build({ viewport: SceneViewportviewport: { SceneViewport.width: numberwidth: 1920, SceneViewport.height: numberheight: 1080 }, fps: numberfps: 30 });
  • ctx.stage checks the actor: one of another blueprint, a value that is not a spawned actor, or an actor of another build throws, naming the input. A subclass of the blueprint passes.
  • An actor input takes no default and has no host form. A host has no actor to pass, so build on such a definition is a type error and throws scene: input "narrator" takes a Narrator actor, which only a stager can pass, at ctx.stage.
  • reflectSceneInputs(outro) reports it as { name: "narrator", actor: "Narrator", required: true }: an actor instead of a type, so an editor shows no form for it.

Exports

What the script returns is the scene’s public surface. Each actor in it resolves, at every frame, to that actor’s state, typed by its class:

const { const card: ResolvedActorOfInstance<Card> | undefinedcard } = 
const compiled: CompiledScene<{
    card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled
.
CompiledScene<{ card: Card; }, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>.at(frame: Frame, options?: FrameResolutionOptions): FrameState<{
    card: Card;
}>
at
(15).
FrameState<{ card: Card; }>.scene: {
    card: ResolvedActorOfInstance<Card> | undefined;
}
scene
;
const card: ResolvedActorOfInstance<Card> | undefinedcard?.label: string | undefinedlabel; // "Hello" const card: ResolvedActorOfInstance<Card> | undefinedcard?.
layout: {
    x: LayoutValue;
    y: LayoutValue;
    width: number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>;
    height: number | `${number}%` | Readonly<{
        kind: "layout.percent";
        value: number;
    }>;
}
layout
.x: LayoutValue | undefinedx; // 0

Return what callers need. A lookup by class or id, on Compiled scenes, is for the actors you did not return. compiled.scene holds the same exports as the live handles the script returned: compiled.scene.card names the card in every frame, and page markup binds it (page bindings).

Was this page helpful?