Drift

Wrap any scene in a slow camera push-in so no frame is ever static

Installation

$ pnpm dlx shadcn@latest add @remocn/drift

Usage

Drift wraps scene content in a constant-speed camera push-in, scaling it from 1 to 1 + grow over the wrapping sequence's duration. The movement is linear on purpose — a constant creep that adds life to a scene without drawing attention to itself.

durationInFrames comes from useVideoConfig(), which is Sequence-scoped. Inside a TransitionSeries.Sequence, the drift stretches exactly across that scene's own duration and peaks right at the cut. Put Drift inside the scene, under the transition, not around the whole series.

A grow of 0.03 to 0.05 is the working range — beyond that it starts to read as a deliberate zoom rather than ambient motion. A negative grow turns it into a slow pull-back instead of a push-in.

import { Drift } from "@/components/remocn/drift";
 
export const MyScene = () => (
  <Drift>
    <YourScene />
  </Drift>
);

Inside a TransitionSeries.Sequence:

import { TransitionSeries, linearTiming } from "@remotion/transitions";
import { Drift } from "@/components/remocn/drift";
import { zoomBlur } from "@/components/remocn/zoom-blur";
 
export const MyVideo = () => (
  <TransitionSeries>
    <TransitionSeries.Sequence durationInFrames={90}>
      <Drift>
        <SceneA />
      </Drift>
    </TransitionSeries.Sequence>
    <TransitionSeries.Transition
      timing={linearTiming({ durationInFrames: 18 })}
      presentation={zoomBlur()}
    />
    <TransitionSeries.Sequence durationInFrames={90}>
      <Drift>
        <SceneB />
      </Drift>
    </TransitionSeries.Sequence>
  </TransitionSeries>
);

Text under drift

A slow push-in keeps every glyph in sub-pixel motion, and a glyph raster can only sit on a whole device pixel — so type inside a plain Drift trembles while borders right next to it glide. Give text blocks their own compositor layer, and the zoom moves a cached texture instead of re-rasterizing the glyphs every frame:

<Drift>
  <div style={{ border: "1px solid rgba(255,255,255,0.08)" }}>
    <div style={{ willChange: "transform" }}>
      <h1>Quarterly Report</h1>
      <p>Revenue grew across every region this quarter.</p>
    </div>
  </div>
</Drift>

One layer per text container — never per word or character, and never on Drift itself. Keep borders and other hairline geometry outside the promoted layer: as part of a resampled texture, a 1px line pulses between sharp and blurry as the scale creeps, while fresh rasterization every frame lets it glide.

Props

PropTypeDefaultDescription
grow
number0.035Scale gain reached at the end of the scene. 0.035 means the scene ends 3.5 percent larger. Negative values pull back instead.