MotionActor

Write motion as code. Resolve any frame as a pure function.

A generator-driven actor runtime for deterministic, frame-based video. The scene generator records what you authored; at(frame) reconstructs it — for any frame, in any order, with the same answer every time.

frame 0·0.00scompiled.at(0).scene.card.progress → 0.0000
card.reveal({ duration: 1 })
30f
ctx.wait(1)
30f
30 fps
0s
1s
2s

The scene above, in full. The player is reading compiled.at(frame).

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

class Card extends LayoutActor {
  progress = signal(0);

  *reveal({ duration }) {
    yield* this.progress.tween(1, duration);
  }
}

const compiled = createScene(function* (ctx) {
  const card = ctx.spawn(Card);

  yield* card.reveal({ duration: 1 }); // 30 frames at 30fps
  yield* ctx.wait(1);                  // 30 more

  return { card };
});

compiled.at(15).scene.card.progress; // 0.5 — no playback, no side effects
compiled.at(15).scene.card.progress; // 0.5 — every single time

Two stages, and that is all

Compile runs your generator once and writes down what it authored. Resolution reconstructs actor state for a frame from that record. Every property worth having follows from keeping them apart.

Sequential to author

The generator reads like a storyboard — do this, wait, then do that. yield* advances an authored cursor; nothing plays.

Random access to render

Frame 400 costs the same whether or not you asked for frame 399. Seeking never replays.

Pure by contract

at(frame) depends on the frame, the channel values it reads, and the options you pass. Nothing else may influence it.

Host values have a door

Measured boxes, physics, fetched data — everything observed enters through a channel, recorded per frame so seeking stays honest.

One hosting model

Slots are named positions; child declarations are their occupants. No structural/mounted split, no second mechanism.

Testable without pixels

Compile a scene, resolve a frame, assert on the actors. No canvas, no browser, no render.

Start reading