---
title: Getting started
description: Install the packages, compile a scene, resolve a frame, and render it with Remotion.
---

## Install

The `@motionactor/*` line is published to **GitHub Packages**, not public npm. Point the
scope at the registry in your project's `.npmrc`:

```ini title=".npmrc"
@motionactor:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}
```

GitHub Packages requires authentication for reads as well as writes, so `NODE_AUTH_TOKEN`
must hold a token with the `read:packages` scope. In CI, `secrets.GITHUB_TOKEN` is enough.

:::warning[Keep the token in the environment]
  `.npmrc` interpolates `${NODE_AUTH_TOKEN}` at install time. Commit the file with the
  variable reference, never with a token in it.
:::

```package-install
pnpm add @motionactor/core @motionactor/dom
```

`@motionactor/core` is the runtime — actors, signals, scene compilation, frame
resolution. `@motionactor/dom` is the DOM/React backend and the Remotion seam. See
[Packages](/docs/packages) for the rest of the line.

## Your first scene

1. ### Declare an actor

    An actor owns state and the commands that move it. `LayoutActor` is the base for anything
    that occupies space — it already declares `layout`, `transform`, `appearance`,
    `flexItem` and `gridItem`.

    ```ts title="scene.ts"
    import { LayoutActor, signal, type Duration } from "@motionactor/core";

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

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

    `reveal` is a generator. `yield*` on a tween is what consumes authored time — the method
    reads like an instruction and compiles into a timeline record.

2. ### Compile a scene

    `createScene` runs the generator once and records everything it authored. It does not
    render, and it does not play.

    ```ts title="scene.ts"
    import { createScene } from "@motionactor/core";

    export const FPS = 30;

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

        yield* card.reveal({ duration: 1 });
        yield* ctx.wait(1);

        return { card };
      },
      { fps: FPS },
    );
    ```

    Returning `{ card }` makes it a **scene export** — typed access to that actor from every
    resolved frame, with no lookup by string and no casts.

3. ### Resolve a frame

    <RevealCardScene />

    The scene above is the code on this page, compiled and running. Drag the scrubber: every
    position calls `compiled.at(frame)` fresh, so scrubbing backwards is the same operation as
    playing forwards.

    ```ts
    compiled.fps; // 30
    compiled.totalDuration; // 60 frames — 1s reveal + 1s hold

    compiled.at(0).scene.card.progress; // 0
    compiled.at(15).scene.card.progress; // 0.5
    compiled.at(30).scene.card.progress; // 1
    ```

    Each call is independent. Asking for frame 30 does not require having asked for frame 29,
    and asking twice returns the same answer.

4. ### Render it

    `defineSceneComposition` wraps a compiled scene as a Remotion composition: it supplies the
    render runtime, binds the Remotion host, and derives duration from the scene through
    `calculateMetadata`.

    ```tsx title="composition.tsx"
    import { AbsoluteFill, Composition, registerRoot } from "remotion";
    import { defineSceneComposition } from "@motionactor/remotion";
    import { compiled, FPS } from "./scene";

    export const CardComposition = defineSceneComposition({
      id: "Card",
      width: 1280,
      height: 720,
      fps: FPS,
      buildScene: () => compiled,
      render: ({ compiled, frame }) => {
        const { card } = compiled.at(frame).scene;

        return (
          <AbsoluteFill className="items-center justify-center bg-slate-950">
            <div
              className="rounded-xl bg-indigo-600 p-8 text-6xl text-white"
              style={{ opacity: card.progress, transform: `scale(${0.9 + card.progress * 0.1})` }}
            >
              MotionActor
            </div>
          </AbsoluteFill>
        );
      },
    });

    export function RemotionRoot() {
      return <Composition {...CardComposition.remotion} />;
    }

    registerRoot(RemotionRoot);
    ```

    The view is a thin reader. It never decides *when* anything happens — it draws the state
    the runtime resolved for this frame.

## What to read next

**[How it works](/docs/mental-model)**

Why compile and resolve are separate stages, and what each one may do.

**[Choosing a primitive](/docs/reactivity)**

signal vs reactiveSignal vs derived vs channel, with the rule for each.

**[Complete Remotion example](/docs/examples/remotion-card)**

The full typechecked example: slots, children, channels, scene exports.

**[Creating actors](/docs/guides/creating-actors)**

Naming, state layout, render paths, and the rules that keep actors reusable.
