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

Rendering

How a resolved frame becomes pixels, through renderers, backends, layers, compositions and the hosts that play them.

A compiled scene holds no pictures. Rendering reads one resolved frame and draws it, and the same code draws that frame in a browser preview, in an editor’s scrubber and in an exported video.

One scene and three layers. The scene spawns the same Dial class into each layer and tweens all three with one call. Each layer belongs to a different backend: the left dial is HTML and CSS, the middle one is Pixi vector strokes on a WebGL canvas, and the right one is a lit Three.js mesh. turn is the only number any of them reads.

The pieces

Piece What it does Made with
Renderer Draws one actor class on one backend dom.render, pixi.render, three.render
Backend A drawing surface and the component that mounts it dom, pixi, three, createBackend
Render runtime The backends and renderers an app uses createRenderRuntime
Layer A region of the stage painted by one backend layer(backend, { frame })
Effect A treatment applied to an actor, drawn by its backend defineEffect, actor.fx, ctx.apply
Transition Morphs a view from before a change to after it transition, <Transition>, <Shared>
Composition Size, frame rate, runtime and scene, ready for a host defineComposition
Host Picks the frame and waits until it is ready to show Remotion, HyperFrames, mountComposition

One frame, start to finish

  1. A host picks a frame. Remotion reads its timeline, the native preview reads its clock, and the exporter takes the lowest frame no capture has claimed yet.
  2. SceneRenderer resolves that frame from the compiled scene and sorts the actors into layers. Actors spawned with ctx.spawn go to the default layer.
  3. Each layer mounts its backend’s host component with the actors it paints: a tree of positioned elements for the DOM, a Pixi Application, a React Three Fiber canvas.
  4. The host gives each actor to the first renderer that matches its class and draws what the renderer returns.
  5. Fonts, images, video and GPU work hold the frame until they are ready. The host shows or captures the frame only after every hold is released.

The dials

Each renderer names its backend and its class. The runtime routes each one to its backend’s layer, so the same Dial actor gets HTML in the DOM layer and a mesh in the Three layer.

import { const createRenderRuntime: (input: CreateRenderRuntimeInput<ReactBackendHost, React.ReactNode>) => ReactRenderRuntime
The 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
,
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 dom: StagedBackend<FC<ReactBackendHostProps>, "dom", ReactNode>
The React DOM backend, and the `dom` binding code names: `dom.render(Box, …)`, `createRenderRuntime({ backends: [dom] })`.
dom
} from "@motionactor/dom/react";
import { const pixi: StagedBackend<FC<StagedHostProps<ReactNode>>, "pixi", ReactNode>
The Pixi backend. It pairs `pixiFilter` with its React stage. A scene-applied effect carries its definition; a wire id resolves from the effect registry, where importing an effect's module installs it.
pixi
} from "@motionactor/pixi/react";
import { const three: Backend<ReactBackendHost, "three">three } from "@motionactor/three/react"; import { class DialDial,
const dials: SceneDefinition<Readonly<Record<never, SceneInput>>, {
    readonly dom: SceneLayer<BackendRef<"dom">>;
    readonly pixi: SceneLayer<BackendRef<"pixi">>;
    readonly three: SceneLayer<BackendRef<"three">>;
}, {
    dials: Dial[];
}>
dials
} from "./dials.scene";
const const TAU: numberTAU = var Math: Math
An intrinsic object that provides basic mathematics functionality and constants.
Math
.Math.PI: number
Pi. This is the ratio of the circumference of a circle to its diameter.
PI
* 2;
const const domDial: BackendRendererDefinition<readonly [typeof Dial], "dom">domDial = const dom: StagedBackend<FC<ReactBackendHostProps>, "dom", ReactNode>
The React DOM backend, and the `dom` binding code names: `dom.render(Box, …)`, `createRenderRuntime({ backends: [dom] })`.
dom
.render: <typeof Dial>(blueprint: typeof Dial, render: ActorRendererForBlueprints<readonly [typeof Dial], "dom">, options?: ActorRendererRegistrationOptions) => BackendRendererDefinition<readonly [typeof Dial], "dom"> (+1 overload)render(class DialDial, ({
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: {
        width: number | `${number}%` | Readonly<{
            kind: "layout.percent";
            value: number;
        }>;
        height: number | `${number}%` | Readonly<...>;
        x: LayoutValue;
        y: LayoutValue;
    };
    ... 6 more ...;
    turn: number;
} & {
    ...;
}
The actor. Reading it makes the renderer depend on all of it.
actor
}) => (
<JSX.IntrinsicElements.div: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>div HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="ring"> <JSX.IntrinsicElements.div: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>div HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="needle" HTMLAttributes<HTMLDivElement>.style?: CSSProperties | undefinedstyle={{ StandardLonghandProperties<string | number, string & {}>.rotate?: Property.Rotate | undefined
This feature is well established and works across many devices and browser versions. It’s been available across browsers since August 2022. **Syntax**: `none | <angle> | [ x | y | z | <number>{3} ] && <angle>` **Initial value**: `none` | Chrome | Firefox | Safari | Edge | IE | | :-----: | :-----: | :------: | :-----: | :-: | | **104** | **72** | **14.1** | **104** | No |
@seehttps://developer.mozilla.org/docs/Web/CSS/Reference/Properties/rotate
rotate
: `${
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: {
        width: number | `${number}%` | Readonly<{
            kind: "layout.percent";
            value: number;
        }>;
        height: number | `${number}%` | Readonly<...>;
        x: LayoutValue;
        y: LayoutValue;
    };
    ... 6 more ...;
    turn: 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: {
        width: number | `${number}%` | Readonly<{
            kind: "layout.percent";
            value: number;
        }>;
        height: number | `${number}%` | Readonly<...>;
        x: LayoutValue;
        y: LayoutValue;
    };
    ... 6 more ...;
    turn: number;
}
signals
.turn: numberturn}turn` }}
/> </JSX.IntrinsicElements.div: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>div> )); const const pixiDial: BackendRendererDefinition<readonly [typeof Dial], "pixi">pixiDial = const pixi: StagedBackend<FC<StagedHostProps<ReactNode>>, "pixi", ReactNode>
The Pixi backend. It pairs `pixiFilter` with its React stage. A scene-applied effect carries its definition; a wire id resolves from the effect registry, where importing an effect's module installs it.
pixi
.render: <typeof Dial>(blueprint: typeof Dial, render: ActorRendererForBlueprints<readonly [typeof Dial], "pixi">, options?: ActorRendererRegistrationOptions) => BackendRendererDefinition<readonly [typeof Dial], "pixi"> (+1 overload)render(class DialDial, ({
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: {
        width: number | `${number}%` | Readonly<{
            kind: "layout.percent";
            value: number;
        }>;
        height: number | `${number}%` | Readonly<...>;
        x: LayoutValue;
        y: LayoutValue;
    };
    ... 6 more ...;
    turn: number;
} & {
    ...;
}
The actor. Reading it makes the renderer depend on all of it.
actor
}) => {
const const r: numberr =
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: {
        width: number | `${number}%` | Readonly<{
            kind: "layout.percent";
            value: number;
        }>;
        height: number | `${number}%` | Readonly<...>;
        x: LayoutValue;
        y: LayoutValue;
    };
    ... 6 more ...;
    turn: number;
} & {
    ...;
}
The actor. Reading it makes the renderer depend on all of it.
actor
.ResolvedActor.localLayout: Readonly<Rect>localLayout.width: numberwidth / 2;
const const angle: numberangle =
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: {
        width: number | `${number}%` | Readonly<{
            kind: "layout.percent";
            value: number;
        }>;
        height: number | `${number}%` | Readonly<...>;
        x: LayoutValue;
        y: LayoutValue;
    };
    ... 6 more ...;
    turn: 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: {
        width: number | `${number}%` | Readonly<{
            kind: "layout.percent";
            value: number;
        }>;
        height: number | `${number}%` | Readonly<...>;
        x: LayoutValue;
        y: LayoutValue;
    };
    ... 6 more ...;
    turn: number;
}
signals
.turn: numberturn * const TAU: numberTAU - var Math: Math
An intrinsic object that provides basic mathematics functionality and constants.
Math
.Math.PI: number
Pi. This is the ratio of the circumference of a circle to its diameter.
PI
/ 2;
return ( <pixiGraphics: PixiReactElementProps<typeof Graphics>pixiGraphics draw: DrawCallbackdraw={(g: Graphicsg) => { g: Graphicsg.Graphics.clear(): Graphics
Clears all drawing commands from the graphics context, effectively resetting it. This includes clearing the current path, fill style, stroke style, and transformations. > [!NOTE] Graphics objects are not designed to be continuously cleared and redrawn. > Instead, they are intended to be used for static or semi-static graphics that > can be redrawn as needed. Frequent clearing and redrawing may lead to performance issues.
@example```ts const graphics = new Graphics(); // Draw some shapes graphics .circle(100, 100, 50) .fill({ color: 0xff0000 }) .rect(200, 100, 100, 50) .fill({ color: 0x00ff00 }); // Clear all graphics graphics.clear(); // Start fresh with new shapes graphics .circle(150, 150, 30) .fill({ color: 0x0000ff }); ```@returnsThe Graphics instance for method chaining@see{@link Graphics#beginPath } For starting a new path without clearing styles@see{@link Graphics#save } For saving the current state@see{@link Graphics#restore } For restoring a previous state
clear
().Graphics.circle(x: number, y: number, radius: number): Graphics
Draws a circle shape at the specified location with the given radius.
@example```ts const graphics = new Graphics(); // Draw a simple filled circle graphics .circle(100, 100, 50) .fill({ color: 0xff0000 }); // Draw a circle with gradient fill const gradient = new FillGradient({ end: { x: 1, y: 0 }, colorStops: [ { offset: 0, color: 0xff0000 }, // Red at start { offset: 0.5, color: 0x00ff00 }, // Green at middle { offset: 1, color: 0x0000ff }, // Blue at end ], }); graphics .circle(250, 100, 40) .fill({ fill: gradient }); ```@paramx - The x-coordinate of the center of the circle@paramy - The y-coordinate of the center of the circle@paramradius - The radius of the circle@returnsThe Graphics instance for method chaining@see{@link Graphics#ellipse } For drawing ellipses@see{@link Graphics#arc } For drawing partial circles
circle
(const r: numberr, const r: numberr, const r: numberr - 3).Graphics.stroke(style?: StrokeInput | undefined): Graphics
Strokes the current path with the current stroke style or specified style. Outlines the shape using the stroke settings.
@example```ts const graphics = new Graphics(); // Stroke with direct color graphics .circle(50, 50, 25) .stroke({ width: 2, color: 0xff0000 }); // 2px red stroke // Fill with texture graphics .rect(0, 0, 100, 100) .stroke(myTexture); // Fill with texture // Stroke with gradient const gradient = new FillGradient({ end: { x: 1, y: 0 }, colorStops: [ { offset: 0, color: 0xff0000 }, { offset: 0.5, color: 0x00ff00 }, { offset: 1, color: 0x0000ff }, ], }); graphics .rect(0, 0, 100, 100) .stroke({ width: 4, fill: gradient, alignment: 0.5, join: 'round' }); ```@paramargs - Optional stroke style to apply. Can be: - A stroke style object with width, color, etc. - A gradient - A pattern If omitted, uses current stroke style.@returnsThe Graphics instance for chaining@see{@link StrokeStyle} For stroke style options@see{@link FillGradient } For gradient strokes@see{@link setStrokeStyle} For setting default stroke style
stroke
({ StrokeAttributes.width?: number | undefined
The width of the stroke in pixels.
@example```ts const stroke = { width: 4 }; ```@default1
width
: 6, FillStyle.color?: ColorSource | undefined
The color to use for the fill. This can be any valid color source, such as a hex value, a Color object, or a string.
@example```ts // Using a hex color const fillStyle = { color: 0xff0000 }; // Red // Using a Color object const fillStyle = { color: new Color(1, 0, 0) }; // Red // Using a string color const fillStyle = { color: 'red' }; // Red // Using object string const fillStyle = { color: 'rgb(255, 0, 0)' }; // Red ```@see{@link ColorSource} For more details on color sources
color
: 0x413c39 });
g: Graphicsg.Graphics.moveTo(x: number, y: number): Graphics
Sets the starting point for a new sub-path. Moves the "pen" to a new location without drawing a line. Any subsequent drawing commands will start from this point.
@example```ts const graphics = new Graphics(); // Create multiple separate lines graphics .moveTo(50, 50) .lineTo(100, 50) .moveTo(50, 100) // Start a new line .lineTo(100, 100) .stroke({ width: 2, color: 0xff0000 }); // Create disconnected shapes graphics .moveTo(150, 50) .rect(150, 50, 50, 50) .fill({ color: 0x00ff00 }) .moveTo(250, 50) // Start a new shape .circle(250, 75, 25) .fill({ color: 0x0000ff }); // Position before curved paths graphics .moveTo(300, 50) .bezierCurveTo( 350, 25, // Control point 1 400, 75, // Control point 2 450, 50 // End point ) .stroke({ width: 3, color: 0xff00ff }); ```@paramx - The x-coordinate to move to@paramy - The y-coordinate to move to@returnsThe Graphics instance for method chaining@see{@link Graphics#lineTo } For drawing lines@see{@link Graphics#beginPath } For starting a completely new path
moveTo
(const r: numberr, const r: numberr)
.Graphics.lineTo(x: number, y: number): Graphics
Connects the current point to a new point with a straight line. Any subsequent drawing commands will start from this new point.
@example```ts const graphics = new Graphics(); // Draw a triangle graphics .moveTo(50, 50) .lineTo(100, 100) .lineTo(0, 100) .fill({ color: 0xff0000 }); // Create a complex shape with multiple lines graphics .moveTo(200, 50) .lineTo(250, 50) .lineTo(250, 100) .lineTo(200, 100) .stroke({ width: 2, color: 0x00ff00 }); ```@paramx - The x-coordinate of the line's end point@paramy - The y-coordinate of the line's end point@returnsThe Graphics instance for method chaining@see{@link Graphics#moveTo } For starting a new sub-path
lineTo
(const r: numberr + var Math: Math
An intrinsic object that provides basic mathematics functionality and constants.
Math
.Math.cos(x: number): number
Returns the cosine of a number.
@paramx A numeric expression that contains an angle measured in radians.
cos
(const angle: numberangle) * const r: numberr, const r: numberr + var Math: Math
An intrinsic object that provides basic mathematics functionality and constants.
Math
.Math.sin(x: number): number
Returns the sine of a number.
@paramx A numeric expression that contains an angle measured in radians.
sin
(const angle: numberangle) * const r: numberr)
.Graphics.stroke(style?: StrokeInput | undefined): Graphics
Strokes the current path with the current stroke style or specified style. Outlines the shape using the stroke settings.
@example```ts const graphics = new Graphics(); // Stroke with direct color graphics .circle(50, 50, 25) .stroke({ width: 2, color: 0xff0000 }); // 2px red stroke // Fill with texture graphics .rect(0, 0, 100, 100) .stroke(myTexture); // Fill with texture // Stroke with gradient const gradient = new FillGradient({ end: { x: 1, y: 0 }, colorStops: [ { offset: 0, color: 0xff0000 }, { offset: 0.5, color: 0x00ff00 }, { offset: 1, color: 0x0000ff }, ], }); graphics .rect(0, 0, 100, 100) .stroke({ width: 4, fill: gradient, alignment: 0.5, join: 'round' }); ```@paramargs - Optional stroke style to apply. Can be: - A stroke style object with width, color, etc. - A gradient - A pattern If omitted, uses current stroke style.@returnsThe Graphics instance for chaining@see{@link StrokeStyle} For stroke style options@see{@link FillGradient } For gradient strokes@see{@link setStrokeStyle} For setting default stroke style
stroke
({ StrokeAttributes.width?: number | undefined
The width of the stroke in pixels.
@example```ts const stroke = { width: 4 }; ```@default1
width
: 6, FillStyle.color?: ColorSource | undefined
The color to use for the fill. This can be any valid color source, such as a hex value, a Color object, or a string.
@example```ts // Using a hex color const fillStyle = { color: 0xff0000 }; // Red // Using a Color object const fillStyle = { color: new Color(1, 0, 0) }; // Red // Using a string color const fillStyle = { color: 'red' }; // Red // Using object string const fillStyle = { color: 'rgb(255, 0, 0)' }; // Red ```@see{@link ColorSource} For more details on color sources
color
: 0xf4f1ef });
}} /> ); }); const const threeDial: BackendRendererDefinition<readonly [typeof Dial], "three">threeDial = const three: Backend<ReactBackendHost, "three">three.render: <typeof Dial>(blueprint: typeof Dial, render: ActorRendererForBlueprints<readonly [typeof Dial], "three">, options?: ActorRendererRegistrationOptions) => BackendRendererDefinition<readonly [typeof Dial], "three"> (+1 overload)render(class DialDial, ({
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: {
        width: number | `${number}%` | Readonly<{
            kind: "layout.percent";
            value: number;
        }>;
        height: number | `${number}%` | Readonly<...>;
        x: LayoutValue;
        y: LayoutValue;
    };
    ... 6 more ...;
    turn: number;
} & {
    ...;
}
The actor. Reading it makes the renderer depend on all of it.
actor
}) => (
<mesh: Mutable<Overwrite<Partial<Overwrite<Mesh<BufferGeometry<NormalBufferAttributes, BufferGeometryEventMap>, Material<MaterialEventMap> | Material<MaterialEventMap>[], Object3DEventMap>, MathProps<Mesh<BufferGeometry<NormalBufferAttributes, BufferGeometryEventMap>, Material<MaterialEventMap> | Material<MaterialEventMap>[], Object3DEventMap>> & ReactProps<...> & Partial<...>>>, Omit<...>>>mesh rotation?: Euler | [x: number, y: number, z: number, order?: EulerOrder | undefined] | Readonly<number | Euler | [x: number, y: number, z: number, order?: EulerOrder | undefined] | undefined>
Object's local rotation ( {@link https://en.wikipedia.org/wiki/Euler_angles Euler angles } ), in radians.
@defaultValue`new THREE.Euler()` - that is `(0, 0, 0, Euler.DEFAULT_ORDER)`.
rotation
={[0,
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: {
        width: number | `${number}%` | Readonly<{
            kind: "layout.percent";
            value: number;
        }>;
        height: number | `${number}%` | Readonly<...>;
        x: LayoutValue;
        y: LayoutValue;
    };
    ... 6 more ...;
    turn: 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: {
        width: number | `${number}%` | Readonly<{
            kind: "layout.percent";
            value: number;
        }>;
        height: number | `${number}%` | Readonly<...>;
        x: LayoutValue;
        y: LayoutValue;
    };
    ... 6 more ...;
    turn: number;
}
signals
.turn: numberturn * const TAU: numberTAU, 0]}>
<torusKnotGeometry: Mutable<Overwrite<Partial<Overwrite<TorusKnotGeometry, MathProps<TorusKnotGeometry> & ReactProps<TorusKnotGeometry>>>, Omit<InstanceProps<TorusKnotGeometry, typeof TorusKnotGeometry>, "object">>>torusKnotGeometry args?: [radius?: number | undefined, tube?: number | undefined, tubularSegments?: number | undefined, radialSegments?: number | undefined, p?: number | undefined, q?: number | undefined] | Readonly<[radius?: number | undefined, tube?: number | undefined, tubularSegments?: number | undefined, radialSegments?: number | undefined, p?: number | undefined, q?: number | undefined] | undefined>args={[1, 0.32, 160, 24]} /> <meshStandardMaterial: Mutable<Overwrite<Partial<Overwrite<MeshStandardMaterial, MathProps<MeshStandardMaterial> & ReactProps<MeshStandardMaterial>>>, Omit<InstanceProps<MeshStandardMaterial, typeof MeshStandardMaterial>, "object">>>meshStandardMaterial color?: string | number | Color | [r: number, g: number, b: number] | [color: ColorRepresentation] | Readonly<Color> | readonly [r: number, g: number, b: number] | readonly [color: ColorRepresentation] | undefined
Color of the material.
@default(1,1,1)
color
="#f86e42" />
</mesh: Mutable<Overwrite<Partial<Overwrite<Mesh<BufferGeometry<NormalBufferAttributes, BufferGeometryEventMap>, Material<MaterialEventMap> | Material<MaterialEventMap>[], Object3DEventMap>, MathProps<Mesh<BufferGeometry<NormalBufferAttributes, BufferGeometryEventMap>, Material<MaterialEventMap> | Material<MaterialEventMap>[], Object3DEventMap>> & ReactProps<...> & Partial<...>>>, Omit<...>>>mesh> )); const const runtime: ReactRenderRuntimeruntime = function createRenderRuntime(input: CreateRenderRuntimeInput<ReactBackendHost, React.ReactNode>): ReactRenderRuntime
The 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, ReactNode>[]backends: [const dom: StagedBackend<FC<ReactBackendHostProps>, "dom", ReactNode>
The React DOM backend, and the `dom` binding code names: `dom.render(Box, …)`, `createRenderRuntime({ backends: [dom] })`.
dom
, const pixi: StagedBackend<FC<StagedHostProps<ReactNode>>, "pixi", ReactNode>
The Pixi backend. It pairs `pixiFilter` with its React stage. A scene-applied effect carries its definition; a wire id resolves from the effect registry, where importing an effect's module installs it.
pixi
, const three: Backend<ReactBackendHost, "three">three],
renderers?: readonly RendererRegistryEntry[] | undefined
Family 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 domDial: BackendRendererDefinition<readonly [typeof Dial], "dom">domDial, const pixiDial: BackendRendererDefinition<readonly [typeof Dial], "pixi">pixiDial, const threeDial: BackendRendererDefinition<readonly [typeof Dial], "three">threeDial],
}); const
const compiled: CompiledScene<{
    dials: Dial[];
}, CompiledLayersOf<{
    readonly dom: SceneLayer<BackendRef<"dom">>;
    readonly pixi: SceneLayer<BackendRef<"pixi">>;
    readonly three: SceneLayer<BackendRef<"three">>;
}>>
compiled
=
const dials: SceneDefinition<Readonly<Record<never, SceneInput>>, {
    readonly dom: SceneLayer<BackendRef<"dom">>;
    readonly pixi: SceneLayer<BackendRef<"pixi">>;
    readonly three: SceneLayer<BackendRef<"three">>;
}, {
    dials: Dial[];
}>
dials
.
SceneDefinition<Readonly<Record<never, SceneInput>>, { readonly dom: SceneLayer<BackendRef<"dom">>; readonly pixi: SceneLayer<BackendRef<"pixi">>; readonly three: SceneLayer<...>; }, { ...; }>.build(args: SceneBuildInput<Readonly<Record<never, SceneInput>>>): CompiledScene<{
    dials: Dial[];
}, CompiledLayersOf<{
    readonly dom: SceneLayer<BackendRef<"dom">>;
    readonly pixi: SceneLayer<BackendRef<"pixi">>;
    readonly three: SceneLayer<BackendRef<"three">>;
}>>
build
({ viewport: SceneViewportviewport: { SceneViewport.width: numberwidth: 1280, SceneViewport.height: numberheight: 480 }, fps: numberfps: 30 });
export const
const Frame: ({ frame }: {
    frame: number;
}) => JSX.Element
Frame
= ({ frame: numberframe }: { 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<{
    dials: Dial[];
}, Readonly<Record<string, CompiledLayer<BackendRef<string>>>>>
compiled
={
const compiled: CompiledScene<{
    dials: Dial[];
}, CompiledLayersOf<{
    readonly dom: SceneLayer<BackendRef<"dom">>;
    readonly pixi: SceneLayer<BackendRef<"pixi">>;
    readonly three: SceneLayer<BackendRef<"three">>;
}>>
compiled
}
frame: numberframe={frame: numberframe} width: numberwidth={1280} height: numberheight={480} runtime?: ReactRenderRuntime | undefinedruntime={const runtime: ReactRenderRuntimeruntime} /> );

The DOM and Pixi backends place each dial’s 240 pixel box from its layout before the renderer runs, so those renderers draw in local coordinates. The Three backend leaves placement to its renderers, and the mesh sits at the origin of its layer’s camera.

What a renderer may do

A renderer reads. It gets the resolved actor (signals, layout, effects, slots, channel values) along with the frame state, the frame number and the frame rate. Everything else is off limits:

  • It keeps no clock. The frame number arrives as a prop, so timers, requestAnimationFrame and Date.now() have no place in render code. The same frame draws the same picture.
  • It never writes authored state. A value only the renderer can know, such as a measured width, goes back into the scene through a channel: the component the renderer returns calls useChannelPublication(actor.channels.width, produce).
  • It draws content, not placement, on the DOM and Pixi backends. Position, size, rotation, opacity and clipping come from the actor’s state and are applied around what it returns.

Was this page helpful?