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

Playing in the browser

SceneMedia plays a compiled scene behind Video.js controls.

@motionactor/player plays a compiled scene on a page. SceneMedia is a Video.js media component: it goes where <Video src> would, and the Video.js player around it supplies the controls, skin, keyboard shortcuts and fullscreen. Every figure on this site plays through it.

import { function createPlayer(config: CreatePlayerConfig<VideoFeatures>): CreatePlayerResult<VideoPlayerStore> (+2 overloads)
Create a player instance with a typed Player component and hooks.
@paramconfig - Player configuration with features and optional display name.@labelVideo
createPlayer
, function Hotkey({ keys, action, value, disabled, target }: HotkeyProps$1): React.ReactNodeHotkey } from "@videojs/react";
import { function VideoSkin(props: VideoSkinProps): import("react").JSX.ElementVideoSkin, const videoFeatures: VideoFeaturesvideoFeatures } from "@videojs/react/video"; import { function SceneMedia(props: SceneMediaProps): JSX.Element
A Video.js media component that plays a compiled scene. Put it where `<Video src>` would go; the player's controls, skin and hotkeys drive it. ```tsx <Player> <VideoSkin> <SceneMedia compiled={compiled} width={1280} height={720}> {({ frame }) => <SceneRenderer compiled={compiled} frame={frame} width={1280} height={720} />} </SceneMedia> </VideoSkin> </Player> ``` A recompiled scene of another length or rate replaces the media: the player keeps the frame on screen (clamped to the new length) and keeps playing.
SceneMedia
} from "@motionactor/player";
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<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 {
const compiled: CompiledScene<{
    heading: Title;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled
} from "./title.scene";
const {
const Player: React.FC<PlayerProps<{
    title: MediaContentValue;
    poster: MediaContentValue;
}>>
Provides a new player store to its descendants without adding a layout element.
Player
} = function createPlayer(config: CreatePlayerConfig<VideoFeatures>): CreatePlayerResult<VideoPlayerStore> (+2 overloads)
Create a player instance with a typed Player component and hooks.
@paramconfig - Player configuration with features and optional display name.@labelVideo
createPlayer
({ CreatePlayerConfig<VideoFeatures>.features: VideoFeatures
Features combined into the player's store, state, actions, and configuration props.
features
: const videoFeatures: VideoFeaturesvideoFeatures });
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, 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
] });
export function function IntroPlayer(): JSX.ElementIntroPlayer() { return ( <
const Player: React.FC<PlayerProps<{
    title: MediaContentValue;
    poster: MediaContentValue;
}>>
Provides a new player store to its descendants without adding a layout element.
Player
>
<function VideoSkin(props: VideoSkinProps): import("react").JSX.ElementVideoSkin> <function SceneMedia(props: SceneMediaProps): JSX.Element
A Video.js media component that plays a compiled scene. Put it where `<Video src>` would go; the player's controls, skin and hotkeys drive it. ```tsx <Player> <VideoSkin> <SceneMedia compiled={compiled} width={1280} height={720}> {({ frame }) => <SceneRenderer compiled={compiled} frame={frame} width={1280} height={720} />} </SceneMedia> </VideoSkin> </Player> ``` A recompiled scene of another length or rate replaces the media: the player keeps the frame on screen (clamped to the new length) and keeps playing.
SceneMedia
SceneMediaProps.compiled: Pick<CompiledScene<void | SceneExportShape, Readonly<Record<string, CompiledLayer<BackendRef<string>>>>>, "fps" | "totalDuration">
The scene's length and rate.
compiled
={
const compiled: CompiledScene<{
    heading: Title;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled
} SceneMediaProps.width: number
Stage size in scene pixels. The stage scales to the player's width.
width
={1920} SceneMediaProps.height: numberheight={1080} SceneMediaProps.loop?: boolean | undefinedloop>
{({ frame: numberframe }) => ( <
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<{
    heading: Title;
}, Readonly<Record<string, CompiledLayer<BackendRef<string>>>>>
compiled
={
const compiled: CompiledScene<{
    heading: Title;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled
} frame: numberframe={frame: numberframe} width: numberwidth={1920} height: numberheight={1080} runtime?: ReactRenderRuntime | undefinedruntime={const runtime: ReactRenderRuntimeruntime} />
)} </function SceneMedia(props: SceneMediaProps): JSX.Element
A Video.js media component that plays a compiled scene. Put it where `<Video src>` would go; the player's controls, skin and hotkeys drive it. ```tsx <Player> <VideoSkin> <SceneMedia compiled={compiled} width={1280} height={720}> {({ frame }) => <SceneRenderer compiled={compiled} frame={frame} width={1280} height={720} />} </SceneMedia> </VideoSkin> </Player> ``` A recompiled scene of another length or rate replaces the media: the player keeps the frame on screen (clamped to the new length) and keeps playing.
SceneMedia
>
<function Hotkey({ keys, action, value, disabled, target }: HotkeyProps$1): React.ReactNodeHotkey HotkeyProps.keys: string
Key pattern to match, such as `Space`, `ArrowRight`, or `Mod+k`.
keys
="." HotkeyProps.action: HotkeyActionName
Player action to run when the key pattern matches.
action
="seekStep" HotkeyProps.value?: number | undefined
Numeric value passed to actions such as `seekStep`, `volumeStep`, and `seekToPercent`. Arrow-key seek and volume actions use their shared input-action step when omitted.
value
={1 /
const compiled: CompiledScene<{
    heading: Title;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled
.CompiledScene<{ heading: Title; }, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>.fps: numberfps} />
<function Hotkey({ keys, action, value, disabled, target }: HotkeyProps$1): React.ReactNodeHotkey HotkeyProps.keys: string
Key pattern to match, such as `Space`, `ArrowRight`, or `Mod+k`.
keys
="," HotkeyProps.action: HotkeyActionName
Player action to run when the key pattern matches.
action
="seekStep" HotkeyProps.value?: number | undefined
Numeric value passed to actions such as `seekStep`, `volumeStep`, and `seekToPercent`. Arrow-key seek and volume actions use their shared input-action step when omitted.
value
={-1 /
const compiled: CompiledScene<{
    heading: Title;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled
.CompiledScene<{ heading: Title; }, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>.fps: numberfps} />
</function VideoSkin(props: VideoSkinProps): import("react").JSX.ElementVideoSkin> </
const Player: React.FC<PlayerProps<{
    title: MediaContentValue;
    poster: MediaContentValue;
}>>
Provides a new player store to its descendants without adding a layout element.
Player
>
); }
  • Import the skin’s styles once with import "@videojs/react/video/skin.css". Video.js themes it through CSS custom properties such as --media-accent-color and --media-border-radius.
  • The stage is width by height scene pixels, scaled to the player’s width. Canvas backends receive that scale as the preview scale, so they draw at the density the viewer sees.
  • SceneMedia provides the frame source and gate. A hold on the gate, from a font, an image or a useHold, keeps the frame on screen and puts the player in its waiting state until the hold is released.
  • A seek lands on a whole frame. The player’s time is rounded to the nearest frame, so a seekStep of 1 / fps moves exactly one frame.
  • The scene is silent. SceneMedia reports no volume, so the skin leaves out its mute button. A scene with Video or Audio actors plays on the native preview.

Reading the frame beside the player

useSceneFrame returns the frame on screen and a seek by frame. It works anywhere under the Video.js Player, including beside the skin, so a readout or a timeline can sit under the video.

import { 
function useSceneFrame(): {
    frame: number;
    seek(frame: number): void;
}
The frame on screen in the nearest player's scene, and a seek by frame. Works anywhere under the Video.js `Player`, beside the skin as well as inside it.
useSceneFrame
} from "@motionactor/player";
export function function FrameReadout(): JSX.ElementFrameReadout() { const { const frame: numberframe, const seek: (frame: number) => voidseek } =
function useSceneFrame(): {
    frame: number;
    seek(frame: number): void;
}
The frame on screen in the nearest player's scene, and a seek by frame. Works anywhere under the Video.js `Player`, beside the skin as well as inside it.
useSceneFrame
();
return <React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefinedonClick={() => const seek: (frame: number) => voidseek(0)}>frame {const frame: numberframe}, back to the start</React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button>; }

Was this page helpful?