Stage

Put a screenshot, recording, or long page on a perspective studio plane and direct the camera across it with timeline keyframes

Installation
$ pnpm dlx shadcn@latest add @remocn/stage

Usage

Stage is both the set and the operator camera. It places one child surface in a perspective studio, then keeps a normalized point on that surface at the center of the shot as moves change over time.

tsx
import { Img, staticFile } from "remotion";
import { Stage } from "@/components/remocn/stage";
 
export const WebsiteTour = () => (
  <Stage
    contentSize={{ width: 1440, height: 9800 }}
    moves={[
      { at: 0, x: 0.5, y: 0.04, zoom: 1.02 },
      { at: 45, x: 0.5, y: 0.04, zoom: 1.02 }, // hold
      { at: 130, x: 0.48, y: 0.5, zoom: 1.12 },
      { at: 220, x: 0.52, y: 0.96, zoom: 1.04 },
    ]}
    shake={0.12}
  >
    <Img
      src={staticFile("long-page.png")}
      style={{ width: "100%", height: "100%" }}
    />
  </Stage>
);

The child can be an image, video, live React component, or whole Remotion scene. Descendants keep animating, but they share one 3D plane. The plane settles into the studio over the first 24 frames; the camera path can continue for as long as the Sequence does.

Long surfaces and camera targets

contentSize declares the surface's intrinsic aspect ratio. Stage fits its width to the composition and allows the resulting height to extend far beyond the frame. It does not fake webpage scrolling: the long surface stays intact while the camera flies above it.

x and y are normalized target points on that surface:

  • x: 0, y: 0 targets the top-left corner.
  • x: 0.5, y: 0.5 targets the center.
  • x: 1, y: 1 targets the bottom-right corner.

Stage clamps x and y to 0–1. Because the current target is held at composition center, the coordinates stay stable when zoom and perspective change. An empty moves array leaves the surface centered and static.

Segment easing and holds

Each destination key can choose the easing used to arrive at it. The default is the project EXPO easing. Repeat an identical pose at a later frame to create a hold.

tsx
import { Easing } from "remotion";
 
<Stage
  contentSize={{ width: 1440, height: 9800 }}
  moves={[
    { at: 0, x: 0.5, y: 0.05, zoom: 1 },
    {
      at: 60,
      x: 0.54,
      y: 0.34,
      zoom: 1.16,
      easing: Easing.out(Easing.cubic),
    },
    { at: 100, x: 0.54, y: 0.34, zoom: 1.16 },
    { at: 170, x: 0.48, y: 0.72, zoom: 1.08 },
  ]}
>
  <YourLongSurface />
</Stage>

Before the first key, Stage holds the first pose; after the last key, it holds the final pose. Keys are sorted by at, and when keys share a frame, the last supplied key wins. Missing pose fields use neutral values (x: 0.5, y: 0.5, zoom: 1, rotate: 0) instead of inheriting from the previous key.

Direct the studio

The studio props remain independent from the camera path:

tsx
<Stage
  contentSize={{ width: 1440, height: 9800 }}
  moves={moves}
  backdrop="radial-gradient(circle at 35% 10%, #403a72, #111117 58%, #08080b)"
  rotateX={14}
  rotateY={-20}
  perspective={900}
  scale={0.86}
  radius={1.8}
  reflection={0.2}
  shadow={0.8}
  light={0.65}
>
  <YourProductScene />
</Stage>

Lower perspective values exaggerate depth. reflection, shadow, and light clamp to 0–1. radius is a percentage of the composition width, so the corner treatment scales across render sizes.

The customizer deliberately exposes experimental ranges: both rotations reach -180–180°, perspective reaches down to 50px, and scale reaches 0.1–3. Rotations beyond 90 degrees reveal the browser's naturally mirrored back face instead of making the surface disappear. Very low perspective and high scale can clip most of the surface; that is intentional for extreme close-ups and abstract depth treatments.

Deterministic handheld movement

shake adds low-frequency operator movement and smoothly interpolated seeded noise. The same seed, frame, and strength always produce the same render.

tsx
<Stage moves={moves} shake={0.18} seed="homepage-take-03">
  <YourScene />
</Stage>

Use 0.08–0.2 for a restrained human operator. shake clamps to 0–1.

Text and source bleed

Fractional zoom continuously resamples glyphs. If small live text trembles, put willChange: "transform" on each text container, not every word. Keep 1px borders outside those promoted layers so borders stay sharp.

The outer frame clips the surface. Targets near an edge, zoom below 1, and strong perspective can reveal the backdrop around it. Leave breathing room in the source or keep edge targets slightly inset, such as y: 0.04 and y: 0.96.

For an optical finish, wrap Stage in CameraLens. Stage supplies the physical set and operator path; CameraLens supplies bloom, softness, chromatic aberration, and vignette.

Props15

PropTypeDefault
children
ReactNode—

The image, video, component, or scene rendered as one studio plane.

contentSize
{ width: number; height: number }composition size

Intrinsic surface dimensions used to preserve its aspect ratio, including long pages.

moves
StageKey[][]

Normalized camera targets keyed by Sequence-local frame.

shake
number0

Deterministic handheld strength, clamped from 0 to 1.

seed
string"remocn-stage"

Seed used by the handheld noise field.

backdrop
string"linear-gradient(145deg, #17181d 0%, #09090b 72%)"

CSS background used for the full-frame studio.

rotateX
number14

X-axis tilt of the surface in degrees.

rotateY
number-20

Y-axis tilt of the surface in degrees.

perspective
number900

CSS perspective distance in pixels. Lower values exaggerate depth.

scale
number0.86

Base scale of the studio plane before keyframe zoom.

radius
number1.4

Surface corner radius as a percentage of composition width.

reflection
number0.24

Floor reflection strength from 0 to 1.

shadow
number0.7

Contact-shadow strength from 0 to 1.

light
number0.55

Directional studio-light strength from 0 to 1.

className
stringundefined

Additional class names applied to the full-frame wrapper.

  • childrenReactNode

    The image, video, component, or scene rendered as one studio plane.

  • contentSize{ width: number; height: number }

    Intrinsic surface dimensions used to preserve its aspect ratio, including long pages.

    Defaultcomposition size

  • movesStageKey[]

    Normalized camera targets keyed by Sequence-local frame.

    Default[]

  • shakenumber

    Deterministic handheld strength, clamped from 0 to 1.

    Default0

  • seedstring

    Seed used by the handheld noise field.

    Default"remocn-stage"

  • backdropstring

    CSS background used for the full-frame studio.

    Default"linear-gradient(145deg, #17181d 0%, #09090b 72%)"

  • rotateXnumber

    X-axis tilt of the surface in degrees.

    Default14

  • rotateYnumber

    Y-axis tilt of the surface in degrees.

    Default-20

  • perspectivenumber

    CSS perspective distance in pixels. Lower values exaggerate depth.

    Default900

  • scalenumber

    Base scale of the studio plane before keyframe zoom.

    Default0.86

  • radiusnumber

    Surface corner radius as a percentage of composition width.

    Default1.4

  • reflectionnumber

    Floor reflection strength from 0 to 1.

    Default0.24

  • shadownumber

    Contact-shadow strength from 0 to 1.

    Default0.7

  • lightnumber

    Directional studio-light strength from 0 to 1.

    Default0.55

  • classNamestring

    Additional class names applied to the full-frame wrapper.

    Defaultundefined

StageKey

PropTypeDefault
at
number—

Required frame from the start of the current Sequence.

x
number0.5

Horizontal target on the surface from left (0) to right (1).

y
number0.5

Vertical target on the surface from top (0) to bottom (1).

zoom
number1

Zoom multiplier around the current target.

rotate
number0

Camera roll in degrees; the surface rotates in the opposite direction.

easing
EasingFunctionEXPO

Easing used by the segment arriving at this key.

  • atnumber

    Required frame from the start of the current Sequence.

  • xnumber

    Horizontal target on the surface from left (0) to right (1).

    Default0.5

  • ynumber

    Vertical target on the surface from top (0) to bottom (1).

    Default0.5

  • zoomnumber

    Zoom multiplier around the current target.

    Default1

  • rotatenumber

    Camera roll in degrees; the surface rotates in the opposite direction.

    Default0

  • easingEasingFunction

    Easing used by the segment arriving at this key.

    DefaultEXPO