Quickstart
Write a scene, read a frame from it, draw it with a renderer and play it in a host.
MotionActor is not released yet.
Write a scene
LayoutActor is the base class for an actor that occupies space; it comes with layout,
transform and appearance state. Card adds a progress signal and a reveal command that
tweens it. The scene spawns one card, reveals it over a second and holds it for another.
yield* is the only thing that spends time. The line beside the stage lights while its frames
play; click a marked line to jump there. The second tab is the renderer that draws the card.
Read a frame
scene is a definition: nothing compiles until a host builds it at a size and a frame rate.
The build is a record of the scene, and nothing in it plays. Ask it for a frame and you get
that frame’s state.
import { const scene: SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
card: Card;
}>
scene } from "./reveal.scene";
using using compiled: CompiledScene<{
card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled = const scene: SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
card: Card;
}>
scene.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: 1280, SceneViewport.height: numberheight: 720 }, fps: numberfps: 30 });
using compiled: CompiledScene<{
card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled.CompiledScene<{ ...; }, CompiledLayersOf<...>>.totalDuration: numbertotalDuration; // 60
using compiled: CompiledScene<{
card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled.CompiledScene<{ ...; }, CompiledLayersOf<...>>.at(frame: Frame, options?: FrameResolutionOptions): FrameState<{
card: Card;
}>
at(15).FrameState<{ ...; }>.scene: {
card: ResolvedActorOfInstance<Card> | undefined;
}
scene.card: ResolvedActorOfInstance<Card> | undefinedcard?.progress: number | undefinedprogress; // 0.5
using compiled: CompiledScene<{
card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled.CompiledScene<{ ...; }, CompiledLayersOf<...>>.at(frame: Frame, options?: FrameResolutionOptions): FrameState<{
card: Card;
}>
at(45).FrameState<{ ...; }>.scene: {
card: ResolvedActorOfInstance<Card> | undefined;
}
scene.card: ResolvedActorOfInstance<Card> | undefinedcard?.progress: number | undefinedprogress; // 1
using compiled: CompiledScene<{
card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled.CompiledScene<{ ...; }, CompiledLayersOf<...>>.at(frame: Frame, options?: FrameResolutionOptions): FrameState<{
card: Card;
}>
at(15).FrameState<{ ...; }>.scene: {
card: ResolvedActorOfInstance<Card> | undefined;
}
scene.card: ResolvedActorOfInstance<Card> | undefinedcard?.progress: number | undefinedprogress; // 0.5 againReturning { card } from the generator makes card a scene export, a typed handle on that
actor in every frame. The order of the calls doesn’t matter: frame 15 reads the same after
frame 45 as it did before.
Draw it
A renderer pairs an actor class with a backend and returns what the backend should draw. A
render runtime gathers backends and renderers, and SceneRenderer resolves one frame and hands
each actor to its renderer.
import { const createRenderRuntime: (input: CreateRenderRuntimeInput<ReactBackendHost, React.ReactNode>) => ReactRenderRuntimeThe floor's `createRenderRuntime` for React: every backend's host is a
React component and its stages wrap React nodes, so mixed backends (each
with its own host props) compose without naming a type.createRenderRuntime } from "@motionactor/renderers/react";
import { const dom: StagedBackend<React.FC<ReactBackendHostProps>, "dom", React.ReactNode>The React DOM backend, and the `dom` binding code names: `dom.render(Box, …)`,
`createRenderRuntime({ backends: [dom] })`.dom } from "@motionactor/dom/react";
import { class CardCard } from "./reveal.scene";
// The backend places the card's box from its layout;
// the renderer draws what goes inside it.
const const card: BackendRendererDefinition<readonly [typeof Card], "dom">card = const dom: StagedBackend<React.FC<ReactBackendHostProps>, "dom", React.ReactNode>The React DOM backend, and the `dom` binding code names: `dom.render(Box, …)`,
`createRenderRuntime({ backends: [dom] })`.dom.render: <typeof Card>(blueprint: typeof Card, render: ActorRendererForBlueprints<readonly [typeof Card], "dom">, options?: ActorRendererRegistrationOptions) => BackendRendererDefinition<readonly [typeof Card], "dom"> (+1 overload)render(class CardCard, ({ actor: ResolvedActor & Omit<ResolvedActor, "signals" | "channels"> & {
compositing: {
blend: "source-over" | "multiply" | "screen" | "overlay" | "darken" | "lighten" | "color-dodge" | "color-burn" | "hard-light" | "soft-light" | "difference" | "exclusion" | "hue" | "saturation" | "color" | "luminosity" | "plus-lighter";
isolate: boolean;
clip: number | null;
};
measuredBox: Rect | undefined;
layout: {
x: LayoutValue;
y: LayoutValue;
width: number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>;
height: number | ... 1 more ... | Readonly<...>;
};
... 6 more ...;
progress: number;
} & {
...;
}
The actor. Reading it makes the renderer depend on all of it.actor }) => {
const { const progress: numberprogress } = actor: ResolvedActor & Omit<ResolvedActor, "signals" | "channels"> & {
compositing: {
blend: "source-over" | "multiply" | "screen" | "overlay" | "darken" | "lighten" | "color-dodge" | "color-burn" | "hard-light" | "soft-light" | "difference" | "exclusion" | "hue" | "saturation" | "color" | "luminosity" | "plus-lighter";
isolate: boolean;
clip: number | null;
};
measuredBox: Rect | undefined;
layout: {
x: LayoutValue;
y: LayoutValue;
width: number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>;
height: number | ... 1 more ... | Readonly<...>;
};
... 6 more ...;
progress: number;
} & {
...;
}
The actor. Reading it makes the renderer depend on all of it.actor.signals: Readonly<Record<string, unknown>> & {
compositing: {
blend: "source-over" | "multiply" | "screen" | "overlay" | "darken" | "lighten" | "color-dodge" | "color-burn" | "hard-light" | "soft-light" | "difference" | "exclusion" | "hue" | "saturation" | "color" | "luminosity" | "plus-lighter";
isolate: boolean;
clip: number | null;
};
measuredBox: Rect | undefined;
layout: {
x: LayoutValue;
y: LayoutValue;
width: number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>;
height: number | ... 1 more ... | Readonly<...>;
};
... 6 more ...;
progress: number;
}
signals;
return (
<React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div
React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="grid size-full place-items-center rounded-2xl
bg-[var(--stage-mark)] text-7xl font-medium text-[var(--stage-on-mark)]"
React.HTMLAttributes<HTMLDivElement>.style?: React.CSSProperties | undefinedstyle={{
StandardLonghandProperties<string | number, string & {}>.opacity?: Property.Opacity | undefinedThis feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
**Syntax**: `<opacity-value>`
**Initial value**: `1`
| Chrome | Firefox | Safari | Edge | IE |
| :----: | :-----: | :----: | :----: | :---: |
| **1** | **1** | **2** | **12** | **9** |opacity: const progress: numberprogress,
StandardLonghandProperties<string | number, string & {}>.transform?: Property.Transform | undefinedThis feature is well established and works across many devices and browser versions. It’s been available across browsers since September 2015.
**Syntax**: `none | <transform-list>`
**Initial value**: `none`
| Chrome | Firefox | Safari | Edge | IE |
| :-----: | :-------: | :-------: | :----: | :-----: |
| **36** | **16** | **9** | **12** | **10** |
| 1 _-x-_ | 3.5 _-x-_ | 3.1 _-x-_ | | 9 _-x-_ |transform: `scale(${0.9 + const progress: numberprogress * 0.1})`,
}}
>
MotionActor
</React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
});
export const const runtime: ReactRenderRuntimeruntime = function createRenderRuntime(input: CreateRenderRuntimeInput<ReactBackendHost, React.ReactNode>): ReactRenderRuntimeThe floor's `createRenderRuntime` for React: every backend's host is a
React component and its stages wrap React nodes, so mixed backends (each
with its own host props) compose without naming a type.createRenderRuntime({
backends: readonly AnyBackend<ReactBackendHost, React.ReactNode>[]backends: [const dom: StagedBackend<React.FC<ReactBackendHostProps>, "dom", React.ReactNode>The React DOM backend, and the `dom` binding code names: `dom.render(Box, …)`,
`createRenderRuntime({ backends: [dom] })`.dom],
renderers?: readonly RendererRegistryEntry[] | undefinedFamily and project renderers, for any backend. Each routes to the backend it
names (`three.render(…)`); one naming a backend missing from
`backends` throws.renderers: [const card: BackendRendererDefinition<readonly [typeof Card], "dom">card],
});
// @filename: frame.tsx
import { const SceneRenderer: <TSceneExports>({ compiled, frame, width, height, sceneMeta, style, children, runtime, }: {
compiled: CompiledScene<TSceneExports>;
frame: number;
width: number;
height: number;
sceneMeta?: unknown;
style?: React.CSSProperties;
children?: React.ReactNode;
runtime?: ReactRenderRuntime;
}) => JSX.Element
Frame-passive renderer for a CompiledScene: resolves
the frame and paints its layers (`SceneLayers`) on a stage of `width` by
`height`. Each layer is painted by the host its backend registers on the
runtime; the default layer by the runtime's default backend. `children` are
page markup: they render in the scope of the scene's DOM layer, where
`<actor.h1 of={compiled.scene.card.title}>` binds a node of it to an actor
(`SceneLayers`).SceneRenderer } from "@motionactor/renderers/react";
import { const runtime: ReactRenderRuntimeruntime } from "./reveal.render";
import type { const scene: SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
card: Card;
}>
scene } from "./reveal.scene";
// The host builds the scene and owns the build: `scene.build({ viewport, fps })`.
type type Build = CompiledScene<{
card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
Build = type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : anyObtain the return type of a function typeReturnType<typeof const scene: SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
card: Card;
}>
scene.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>;
export const const Frame: ({ compiled, frame }: {
compiled: Build;
frame: number;
}) => JSX.Element
Frame = ({ compiled: CompiledScene<{
card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled, frame: numberframe }: { compiled: CompiledScene<{
card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled: type Build = CompiledScene<{
card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
Build; frame: numberframe: number }) => (
<const SceneRenderer: <TSceneExports>({ compiled, frame, width, height, sceneMeta, style, children, runtime, }: {
compiled: CompiledScene<TSceneExports>;
frame: number;
width: number;
height: number;
sceneMeta?: unknown;
style?: React.CSSProperties;
children?: React.ReactNode;
runtime?: ReactRenderRuntime;
}) => JSX.Element
Frame-passive renderer for a CompiledScene: resolves
the frame and paints its layers (`SceneLayers`) on a stage of `width` by
`height`. Each layer is painted by the host its backend registers on the
runtime; the default layer by the runtime's default backend. `children` are
page markup: they render in the scope of the scene's DOM layer, where
`<actor.h1 of={compiled.scene.card.title}>` binds a node of it to an actor
(`SceneLayers`).SceneRenderer
compiled: CompiledScene<{
card: Card;
}, Readonly<Record<string, CompiledLayer<BackendRef<string>>>>>
compiled={compiled: CompiledScene<{
card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled}
frame: numberframe={frame: numberframe}
width: numberwidth={1280}
height: numberheight={720}
runtime?: ReactRenderRuntime | undefinedruntime={const runtime: ReactRenderRuntimeruntime}
/>
);The DOM backend positions the card from its layout and transform state and applies its
appearance. The renderer draws only what goes inside that box.
Play it
A composition is what a host plays: a size, a frame rate, a render runtime, the scene it builds
at that size and rate, and a way to draw a frame. toRemotionComposition turns one into a
Remotion <Composition>, which reads the duration from the build.
import { const Composition: <Schema extends AnyZodObject, Props extends Record<string, unknown>>(props: CompositionProps<Schema, Props>) => import("react/jsx-runtime").JSX.Element | nullComposition } from "remotion";
import { const defineComposition: DefineComposition<ReactBackendHost, React.ReactNode, React.ReactElement<unknown, string | React.JSXElementConstructor<any>>>A composition of a `createScene` definition (`scene`), painted by `render`,
or by default by `SceneRenderer` over `background`.defineComposition, const SceneRenderer: <TSceneExports>({ compiled, frame, width, height, sceneMeta, style, children, runtime, }: {
compiled: CompiledScene<TSceneExports>;
frame: number;
width: number;
height: number;
sceneMeta?: unknown;
style?: React.CSSProperties;
children?: React.ReactNode;
runtime?: ReactRenderRuntime;
}) => JSX.Element
Frame-passive renderer for a CompiledScene: resolves
the frame and paints its layers (`SceneLayers`) on a stage of `width` by
`height`. Each layer is painted by the host its backend registers on the
runtime; the default layer by the runtime's default backend. `children` are
page markup: they render in the scope of the scene's DOM layer, where
`<actor.h1 of={compiled.scene.card.title}>` binds a node of it to an actor
(`SceneLayers`).SceneRenderer } from "@motionactor/renderers/react";
import { function toRemotionComposition<P extends Record<string, unknown>, T>(input: CompositionDefinition<P, T>): {
id: string;
width: number;
height: number;
fps: number;
defaultProps: P;
durationInFrames: number;
component: React.FC<P>;
calculateMetadata: CalculateMetadataFunction<P>;
}
A Remotion composition for `input`: like every host, it mounts the runtime media family after the runtime's own renderers.
The mounted instance builds at Remotion's resolved viewport, which `calculateMetadata` or a Player may set apart from the definition's.toRemotionComposition } from "@motionactor/remotion";
import { const scene: SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
card: Card;
}>
scene } from "./reveal.scene";
import { const runtime: ReactRenderRuntimeruntime } from "./reveal.render";
export const const reveal: CompositionDefinition<HostInputArgs<Readonly<Record<never, SceneInput>>>, {
card: Card;
}, ReactBackendHost, React.ReactNode, React.ReactElement<unknown, string | React.JSXElementConstructor<any>>>
reveal = defineComposition<SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
card: Card;
}>>(definition: SceneCompositionInput<SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
card: Card;
}>, ReactBackendHost, React.ReactNode, React.ReactElement<...>>): CompositionDefinition<...> (+1 overload)
`scene`'s inputs are the props.defineComposition({
id: stringid: "Reveal",
width: numberwidth: 1280,
height: numberheight: 720,
fps: numberfps: 30,
runtime: RenderRuntime<ReactBackendHost, React.ReactNode>runtime,
scene: SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
card: Card;
}>
scene,
render?(args: CompositionRenderArgs<HostInputArgs<Readonly<Record<never, SceneInput>>>, {
card: Card;
}>): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>
render: ({ compiled: CompiledScene<{
card: Card;
}, Readonly<Record<string, CompiledLayer<BackendRef<string>>>>>
compiled, frame: numberframe, width: numberwidth, height: numberheight }) => (
<const SceneRenderer: <TSceneExports>({ compiled, frame, width, height, sceneMeta, style, children, runtime, }: {
compiled: CompiledScene<TSceneExports>;
frame: number;
width: number;
height: number;
sceneMeta?: unknown;
style?: React.CSSProperties;
children?: React.ReactNode;
runtime?: ReactRenderRuntime;
}) => JSX.Element
Frame-passive renderer for a CompiledScene: resolves
the frame and paints its layers (`SceneLayers`) on a stage of `width` by
`height`. Each layer is painted by the host its backend registers on the
runtime; the default layer by the runtime's default backend. `children` are
page markup: they render in the scope of the scene's DOM layer, where
`<actor.h1 of={compiled.scene.card.title}>` binds a node of it to an actor
(`SceneLayers`).SceneRenderer
compiled: CompiledScene<{
card: Card;
}, Readonly<Record<string, CompiledLayer<BackendRef<string>>>>>
compiled={compiled: CompiledScene<{
card: Card;
}, Readonly<Record<string, CompiledLayer<BackendRef<string>>>>>
compiled}
frame: numberframe={frame: numberframe}
width: numberwidth={width: numberwidth}
height: numberheight={height: numberheight}
/>
),
});
export const const RemotionRoot: () => JSX.ElementRemotionRoot = () => (
<const Composition: <Schema extends AnyZodObject, Props extends Record<string, unknown>>(props: CompositionProps<Schema, Props>) => import("react/jsx-runtime").JSX.Element | nullComposition {...toRemotionComposition<HostInputArgs<Readonly<Record<never, SceneInput>>>, {
card: Card;
}>(input: CompositionDefinition<HostInputArgs<Readonly<Record<never, SceneInput>>>, {
card: Card;
}>): {
id: string;
width: number;
height: number;
fps: number;
defaultProps: HostInputArgs<Readonly<Record<never, SceneInput>>>;
durationInFrames: number;
component: React.FC<HostInputArgs<Readonly<Record<never, SceneInput>>>>;
calculateMetadata: CalculateMetadataFunction<...>;
}
A Remotion composition for `input`: like every host, it mounts the runtime media family after the runtime's own renderers.
The mounted instance builds at Remotion's resolved viewport, which `calculateMetadata` or a Player may set apart from the definition's.toRemotionComposition(const reveal: CompositionDefinition<HostInputArgs<Readonly<Record<never, SceneInput>>>, {
card: Card;
}, ReactBackendHost, React.ReactNode, React.ReactElement<unknown, string | React.JSXElementConstructor<any>>>
reveal)} />
);The same definition plays in MotionActor’s own preview, which waits for fonts and media to be ready before it shows a frame:
import { function mountComposition<P extends Record<string, unknown>, T>(options: Omit<CompositionHostOptions<P, T>, "mediaAdapter" | "preparedMedia"> & VideoDecodingOptions & AudioDecodingOptions & {
onError?(error: unknown): void;
}): Promise<{
play: () => Promise<void>;
pause: () => void;
dispose: (reason?: unknown) => void;
seek(frame: number, mode?: PresentMode): Promise<void>;
getSnapshot: () => PreviewSnapshot;
... 8 more ...;
updateEditor(next: unknown): Promise<...>;
}>
Native local preview. Frame readiness and one media inventory own playback.mountComposition } from "@motionactor/engine/react";
import { const reveal: CompositionDefinition<HostInputArgs<Readonly<Record<never, SceneInput>>>, {
card: Card;
}, ReactBackendHost, ReactNode, ReactElement<unknown, string | JSXElementConstructor<any>>>
reveal } from "./composition";
const const preview: {
play: () => Promise<void>;
pause: () => void;
dispose: (reason?: unknown) => void;
seek(frame: number, mode?: PresentMode): Promise<void>;
getSnapshot: () => PreviewSnapshot;
subscribe(listener: () => void): () => void;
readonly compiled: CompiledScene<{
card: Card;
}, Readonly<Record<string, CompiledLayer<BackendRef<string>>>>>;
readonly mediaPlan: MediaPlan;
readonly composition: CompositionDefinition<{}, {
card: Card;
}>;
... 4 more ...;
updateEditor(next: unknown): Promise<...>;
}
preview = await mountComposition<{}, {
card: Card;
}>(options: (Omit<CompositionHostOptions<{}, {
card: Card;
}>, "mediaAdapter" | "preparedMedia"> & VideoDecodingOptions) & (AudioDecodingOptions & {
onError?(error: unknown): void;
})): Promise<{
play: () => Promise<void>;
pause: () => void;
dispose: (reason?: unknown) => void;
seek(frame: number, mode?: PresentMode): Promise<void>;
getSnapshot: () => PreviewSnapshot;
subscribe(listener: () => void): () => void;
... 7 more ...;
updateEditor(next: unknown): Promise<...>;
}>
Native local preview. Frame readiness and one media inventory own playback.mountComposition({
composition: CompositionDefinition<{}, {
card: Card;
}>
composition: const reveal: CompositionDefinition<HostInputArgs<Readonly<Record<never, SceneInput>>>, {
card: Card;
}, ReactBackendHost, ReactNode, ReactElement<unknown, string | JSXElementConstructor<any>>>
reveal,
element: HTMLElementelement: var document: Document**`window.document`** returns a reference to the document contained in the window.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/document)document.ParentNode.querySelector<HTMLElement>(selectors: string): HTMLElement | null (+4 overloads)Returns the first element that is a descendant of node that matches selectors.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/querySelector)querySelector("#stage")!,
props: {}props: {},
});
await const preview: {
play: () => Promise<void>;
pause: () => void;
dispose: (reason?: unknown) => void;
seek(frame: number, mode?: PresentMode): Promise<void>;
getSnapshot: () => PreviewSnapshot;
subscribe(listener: () => void): () => void;
readonly compiled: CompiledScene<{
card: Card;
}, Readonly<Record<string, CompiledLayer<BackendRef<string>>>>>;
readonly mediaPlan: MediaPlan;
readonly composition: CompositionDefinition<{}, {
card: Card;
}>;
... 4 more ...;
updateEditor(next: unknown): Promise<...>;
}
preview.function seek(frame: number, mode?: PresentMode): Promise<void>Pauses, then resolves once `frame` is on screen; with `playing`, once it is
complete, painting at the next refresh (for a player keeping its own time).seek(45);
const preview: {
play: () => Promise<void>;
pause: () => void;
dispose: (reason?: unknown) => void;
seek(frame: number, mode?: PresentMode): Promise<void>;
getSnapshot: () => PreviewSnapshot;
subscribe(listener: () => void): () => void;
readonly compiled: CompiledScene<{
card: Card;
}, Readonly<Record<string, CompiledLayer<BackendRef<string>>>>>;
readonly mediaPlan: MediaPlan;
readonly composition: CompositionDefinition<{}, {
card: Card;
}>;
... 4 more ...;
updateEditor(next: unknown): Promise<...>;
}
preview.play: () => Promise<void>play();