agentsclimarketplace

Remotion video

Skill Gekkos-tech/agency-os/plugins/motion-3d/skills/remotion-video

Best practices for building programmatic videos in React with Remotion v4+. Use when the user mentions "Remotion", "video in React", "programmatic video", "render a video from code", "generate an MP4 from React components", or wants data-driven, templated, or automated video production. Covers the frame-based mental model, compositions, deterministic rendering, interpolate/spring animation, Sequence/Series timing, assets and audio, performance, and CLI rendering. Not for browser animation of live websites — for animating DOM in a running web page (scroll effects, hover states, page transitions), use a GSAP skill instead; Remotion renders video files.From its SKILL.md

Install
npx -y skills add Gekkos-tech/agency-os --skill remotion-video

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

9.4 KB, ~2.2k tokens by cl100k_base, as published. Nobody here has run it

Remotion Video

Remotion turns React components into rendered video files. Every rule below exists because rendering happens frame by frame in headless browser instances: anything non-deterministic or time-based (instead of frame-based) produces flicker, desync, or broken renders.

Core mental model

  • A video is a pure function of the current frame: frame -> pixels. The same frame number must always produce the same image, on any machine, at any time, in any order.
  • Read the current frame with useCurrentFrame(). Read fps, duration, width, height with useVideoConfig().
  • Never animate with wall-clock time, setTimeout, requestAnimationFrame, CSS transitions, or CSS animations. During rendering there is no continuous time — frames are captured independently and possibly in parallel. All motion derives from frame.
const frame = useCurrentFrame();
const { fps, durationInFrames, width, height } = useVideoConfig();
const opacity = interpolate(frame, [0, 30], [0, 1], {
  extrapolateLeft: "clamp",
  extrapolateRight: "clamp",
});

Compositions

Register videos in the root file with <Composition>:

<Composition
  id="Promo"
  component={Promo}
  durationInFrames={30 * 10} // 10 s at 30 fps
  fps={30}
  width={1920}
  height={1080}
  defaultProps={{ title: "Hello" }}
/>
  • id must be unique and is what you pass to the render CLI.
  • Express durations in terms of fps (fps * seconds), never magic frame counts.
  • Use <Still> for single-frame outputs (thumbnails, OG images).
  • For data-driven videos, use calculateMetadata on the composition to fetch data once and derive durationInFrames / props from it — do not fetch inside components per frame.
<Composition
  id="Podcast"
  component={Podcast}
  calculateMetadata={async ({ props }) => {
    const data = await fetchEpisode(props.episodeId);
    return {
      props: { ...props, data },
      durationInFrames: Math.ceil(data.durationSeconds * 30),
    };
  }}
  ...
/>

Determinism rules

  • No Math.random(). Use random(seed) from remotion — deterministic per seed. Give each element a stable seed (random(particle-${i})).
  • No Date.now(), new Date(), performance.now() influencing visuals.
  • No CSS transition or animation properties. Set styles directly from interpolated values each frame.
  • No useEffect-driven animation state (counters, tick loops). Derive everything from useCurrentFrame().
  • Data fetching inside a component must be wrapped in delayRender() / continueRender():
const [handle] = useState(() => delayRender("fetch data"));
useEffect(() => {
  fetchData()
    .then((d) => { setData(d); continueRender(handle); })
    .catch((err) => cancelRender(err));
}, []);

Prefer calculateMetadata over in-component fetching: it runs once, not once per render instance, and avoids flicker from divergent responses across the parallel browser tabs used for rendering.

interpolate() and spring()

  • Always clamp unless overshoot is intentional: { extrapolateLeft: "clamp", extrapolateRight: "clamp" }. Unclamped interpolations are the most common source of elements flying off-screen before/after their animation window.
  • Input ranges must be monotonically increasing. Multi-stop ranges express keyframes: interpolate(frame, [0, 20, 80, 100], [0, 1, 1, 0]) = fade in, hold, fade out.
  • Add easing via Easing from remotion: { easing: Easing.out(Easing.cubic) }.
  • spring() produces natural motion from 0 to 1; pass fps from useVideoConfig():
const scale = spring({ frame, fps, config: { damping: 200 } });
  • damping: 200 = smooth, no overshoot (good default for UI). Lower damping = bouncier. Use durationInFrames on spring (v4) to fit it into a fixed slot.
  • Combine: drive interpolate with a spring's output to map 0-1 onto any range.

See references/animation-patterns.md for ready-made recipes (fade, slide, scale-in, typewriter, counter, staggered lists).

Sequencing

  • <Sequence from={N} durationInFrames={M}> shifts its children's local frame to 0 at frame N and unmounts them outside the window. Inside a Sequence, useCurrentFrame() returns the local frame — write child animations starting at 0.
  • <Series> with <Series.Sequence durationInFrames={...}> chains scenes back-to-back without manual offset arithmetic. Negative offset overlaps scenes.
  • Stagger repeated elements by rendering each in its own Sequence or by offsetting the frame per index:
{items.map((item, i) => (
  <Sequence key={item.id} from={i * 5}>
    <Item item={item} />
  </Sequence>
))}
  • Use premountFor={frames} on Sequence when children load assets, so they mount (invisible) before appearing and are ready on their first visible frame.

Assets

  • Put files in public/ and reference them with staticFile("name.png"). Never use relative import paths or absolute filesystem paths for media.
  • Use Remotion's media components, never bare HTML tags: <Img>, <OffthreadVideo>, <Audio>, <Video>. They ensure the asset is fully loaded before the frame is captured (bare <img>/<video> cause flicker and black frames).
  • Prefer <OffthreadVideo> over <Video> for renders — it extracts frames via FFmpeg and is more reliable and seek-accurate.
  • Fonts: use @remotion/google-fonts (loadFont() from e.g. @remotion/google-fonts/Inter) for Google fonts, or @remotion/fonts loadFont() with staticFile() for local font files. Do not rely on CSS @import of font CDNs.
  • Preload remote assets with @remotion/preload (preloadVideo, preloadAudio) for smoother Player playback.

Details in references/rendering-and-assets.md.

Audio

  • <Audio src={staticFile("music.mp3")} />; place inside a <Sequence> to delay its start.
  • Trim with trimBefore / trimAfter (frames of the source to cut).
  • Volume ramps: pass a function of frame — volume={(f) => interpolate(f, [0, 30], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" })}. The function receives the frame relative to audio start. Use this for fade-in/fade-out and ducking under voiceover.
  • playbackRate changes speed; loop repeats the clip.

Performance

  • Every frame is a fresh React render. Keep per-frame work cheap: memoize heavy computation with useMemo keyed on inputs that actually change, not on frame.
  • Avoid layout thrash: animate transform and opacity rather than width/top/left where possible.
  • Move expensive one-time work (parsing data, building lookup tables) outside components or into calculateMetadata.
  • Do not mount huge trees that are invisible — Sequences unmount children outside their window; use that.
  • JSON/data props passed through compositions should be small; fetch big payloads in calculateMetadata.

Rendering

  • Preview: npx remotion studio.
  • Render: npx remotion render <composition-id> out/video.mp4. Props via --props='{"title":"Hi"}' or a JSON file path.
  • Stills: npx remotion still <id> out/frame.png --frame=120.
  • --concurrency controls parallel browser tabs (default ~half the CPU cores); raise for CPU-bound speedups, lower if memory-constrained.
  • Common flags: --codec=h264 (default MP4), --crf for quality, --frames=0-89 to render a range.
  • Server-side/programmatic rendering: @remotion/renderer (bundle, selectComposition, renderMedia); cloud-scale via @remotion/lambda.

Common pitfalls

  • Flicker or elements "jumping": non-deterministic data (unseeded random, fetch races across render workers). Fix with random(seed), calculateMetadata, or delayRender.
  • Black/blank video frames: bare <video> tag or missing staticFile — use <OffthreadVideo>.
  • Animation ignores easing/looks linear in output but fine in Player: CSS transitions were doing the work in the browser; replace with frame interpolation.
  • Element visible before its animation starts: missing extrapolation clamp.
  • Spring feels wrong at different fps: fps not passed from useVideoConfig().

Companion packages — when to reach for them

  • @remotion/transitions: scene-to-scene transitions (<TransitionSeries> with fade/slide/wipe presentations) instead of hand-rolling crossfades.
  • @remotion/shapes: parametric SVG shapes (<Circle>, <Star>, <Pie>, <Triangle>) for motion-graphic primitives.
  • @remotion/lottie: play After Effects/Lottie JSON synced to the Remotion timeline.
  • @remotion/gif, @remotion/noise, @remotion/paths: GIF playback synced to frames, deterministic noise fields, SVG path interpolation/measurement.

Scope boundary

Remotion is for producing video files (or embeddable <Player> previews of them). If the goal is animating a live website — scroll-triggered effects, hover micro-interactions, page transitions — that is browser animation: use GSAP (see the gsap-* skills), not Remotion.

What ships with it: 2 files

12.5 KB alongside SKILL.md

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.