calesthio/remotion-video-composition
Provider-independent production workflow for assembling generated or sourced media into React/Remotion videos. Use when an agent must plan, build, render, review, variant-render, or hand off Remotion compositions with media custody, captions, audio, animation, accessibility, provenance, QA, and delivery requirements.
npx skills add https://github.com/calesthio/generative-media-skills --skill remotion-video-composition
Use this skill when Remotion is the composition layer for a real video deliverable: generated-media explainers, product videos, social variants, data-driven videos, screen-demo composites, captioned talking-head recuts, or any render where React code, assets, captions, audio, and delivery metadata must become a reproducible video package.
This is a production skill, not just a coding recipe. Treat Remotion as the deterministic assembly and finishing engine around media that may have been generated elsewhere.
Facts below are based on primary sources verified on 2026-07-11 unless noted otherwise:
Volatile facts: Remotion package versions, API availability, licensing and pricing, cloud render limits, browser renderer status, package deprecations, social-platform delivery specs, caption file requirements, and AI/provider usage rights. Re-check those at production time and record the verification date in the project handoff.
Remotion is strongest when the video can be expressed as deterministic React over a timeline:
Do not use Remotion as a substitute for missing creative decisions. Before writing code, lock the brief, visual system, asset list, caption approach, audio plan, target aspect ratio, duration, and delivery requirements.
Escalate instead of guessing when:
Collect or derive these before composition work:
If the user provides only a vague "make a video" brief, first produce a short proposal and ask for missing decisions that materially affect the result. Do not begin asset-heavy implementation until the composition contract is coherent.
Create a composition project whose code mirrors the production contract.
Recommended structure:
project/
+- package.json
+- remotion.config.ts
+- public/
| +- assets/
| | +- image/
| | +- video/
| | +- audio/
| | +- fonts/
| | +- captions/
| +- provenance/
+- src/
| +- Root.tsx
| +- compositions/
| +- scenes/
| +- components/
| +- data/
| +- styles/
| +- utils/
+- review/
+- renders/
+- handoff/
Keep creative data separate from rendering mechanics:
src/Root.tsx registers compositions.src/data/*.json or typed modules define scenes, variants, captions, and asset references.src/scenes/* render individual scenes from data.src/components/* hold reusable layout primitives, not one-off scene decisions.public/assets/* contains frozen media used by staticFile().handoff/ contains final outputs, caption sidecars, stems, provenance ledger, and render notes.Use stable identifiers for every asset, scene, caption track, and variant. These IDs should survive code refactors and appear in filenames, review notes, and provenance records.
In Remotion, a composition is the renderable video and is registered with properties such as durationInFrames, fps, width, and height. Keep these values explicit and derived from the production contract, not scattered through components.
Use:
<Composition> to register renderable videos.<Sequence> or <Series> to place scenes on the timeline.useCurrentFrame() for frame-relative animation.useVideoConfig() for fps, durationInFrames, width, and height.calculateMetadata() when duration, dimensions, fps, codec, or props must be computed from input data.Frame math:
export const secondsToFrames = (seconds: number, fps: number) =>
Math.round(seconds * fps);
Use the same helper everywhere. For exact broadcast rates such as 23.976 or 29.97, decide whether the project will represent timing as integer frame counts from an edit decision list rather than repeatedly converting decimal seconds.
When scenes are sequential, compute from values from accumulated frame durations rather than hardcoding them. Hardcoded frame offsets are acceptable only for tiny one-off compositions after review.
Example:
import {Composition, Series} from 'remotion';
import {z} from 'zod';
import {MainVideo} from './compositions/MainVideo';
import {projectSchema, defaultProject} from './data/project';
export const RemotionRoot = () => (
<Composition
id="MainVideo"
component={MainVideo}
fps={30}
width={1920}
height={1080}
durationInFrames={defaultProject.totalFrames}
defaultProps={defaultProject}
schema={projectSchema}
calculateMetadata={({props}) => ({
durationInFrames: props.scenes.reduce((sum, s) => sum + s.frames, 0),
props,
})}
/>
);
Remotion input props must be JSON-serializable when used for rendering. Use a Zod schema for variant data so a bad render fails before frames are produced.
Select a canonical composition target before layout:
Do not assume one master crop will work everywhere. Compose either:
width and height.Use safe-area constants and design to them:
export const makeSafeArea = (width: number, height: number) => ({
x: Math.round(width * 0.07),
y: Math.round(height * 0.07),
w: Math.round(width * 0.86),
h: Math.round(height * 0.86),
});
Keep critical text, logos, captions, and calls to action inside the safe area unless the platform-specific spec says otherwise. Platform UI overlays change; re-check current platform templates or client delivery specs at export time.
Freeze inputs before coding against them.
For each asset, record:
public/assets;Use Remotion staticFile() for files in the project public/ directory. This avoids fragile absolute paths and works across Studio and render environments. For embedded video, check the installed Remotion version and choose the current recommended media component for that version: current docs recommend <Video> from @remotion/media for new code, while <OffthreadVideo> remains useful when you specifically need its FFmpeg-backed frame extraction behavior.
Do not leave final composition dependencies in temp folders, downloads, cloud URLs, or external drives. Remote URLs are acceptable for quick exploration only; production renders should use frozen local copies unless the pipeline explicitly requires remote fetches and records their immutable version.
Preflight every media file:
Caption strategy is a production decision:
For prerecorded synchronized media with meaningful audio, WCAG 1.2.2 expects captions that represent spoken content and meaningful non-speech information. Do not provide dialogue-only subtitles when sound effects, speaker identity, or music cues are needed to understand the video.
Recommended workflow:
Remotion provides caption utilities through @remotion/captions; re-check the installed package and API version before relying on helpers such as SRT parsing or TikTok-style segmentation.
Caption design rules:
[door closes] or [music swells];Plan audio as stems:
In Remotion, align audio using the same frame timeline as visuals. Use <Sequence> to delay stems and use the media component volume prop for static or frame-varying levels. Multiple audio tags can be mixed, but still review the final render with audio tools; visual preview is not enough.
Production heuristics:
If using FFmpeg for post-mix or loudness checks, use official filter documentation for loudnorm, amix, atrim, afade, and related filters. Do not hardcode a universal LUFS target; platform, broadcast, podcast, and client specs differ. If the spec is unknown, flag the chosen target as a production assumption.
Keep animation deterministic and frame-based:
useCurrentFrame() and useVideoConfig();spring() for physically plausible reveals;interpolate() and interpolateColors() for value mapping;random(seed) for deterministic variation;Math.random(), Date.now(), unstable network fetches during render, and layout that depends on browser timing.Design motion from the message:
For scene transitions, first decide whether the cut is conceptual, spatial, rhythmic, or purely editorial. Then choose a transition family:
If using @remotion/transitions or any transition package, verify the current API and version at production time.
Treat text as designed media:
Use layout primitives that respond to composition dimensions:
Frame: establishes background and safe area;Grid or Stack: controls spacing;CaptionLayer: reserves caption position;BrandBug: controls logo placement;SceneChrome: optional scene label, source note, or progress indicator;MediaSlot: covers fit/crop decisions for images or video.Do not rely on CSS responsive behavior alone. Render review stills at multiple frames and aspect ratios to catch clipped text, invisible overlays, and captions competing with UI.
Remotion is well-suited to batch variants if the creative system is explicit.
Use variants when changing:
Variant rules:
Do not batch-render many variants until one representative variant has passed visual, caption, audio, and accessibility review.
Use the render path that matches the environment:
@remotion/renderer renderMedia() for programmatic Node/Bun rendering;Choose output settings from the delivery spec:
Remotion renderMedia() and CLI render expose codec and output-location choices. Remotion's quality guidance identifies CRF as a main quality control for supported codecs, while FFmpeg documentation governs underlying codec and filter behavior. Lower compression usually increases quality and file size; always verify visually after export.
Use deterministic review exports before final:
If a render fails, classify the failure:
Then fix the class of problem rather than blindly retrying.
Before a serious render:
public/;Avoid:
If using calculateMetadata() for data fetching, follow current Remotion guidance and avoid repeated rate-limited calls in highly concurrent renders. Cache or freeze fetched data into project files when practical.
Run these checks before delivery:
Automated checks do not replace human review for captions, flashing risk, or comprehension.
Maintain a provenance ledger even if the final file is not C2PA-signed.
Minimum ledger fields:
{
"project_id": "example-video",
"verified_on": "2026-07-11",
"outputs": [
{
"id": "main-16x9-v03",
"path": "handoff/main-16x9-v03.mp4",
"sha256": "..."
}
],
"assets": [
{
"id": "hero-image-01",
"local_path": "public/assets/image/hero-image-01.png",
"source": "generated",
"provider": "example-provider",
"model": "example-model",
"prompt_or_brief": "stored in secure project notes",
"seed": "12345",
"license_or_permission": "client approved for this campaign",
"restrictions": "no standalone resale",
"sha256": "..."
}
],
"software": {
"remotion": "record installed version",
"ffmpeg": "record installed version"
},
"human_approvals": [
{
"scope": "final render",
"approver": "client/contact",
"date": "YYYY-MM-DD"
}
]
}
C2PA Content Credentials are a technical provenance standard built around manifests, assertions, claims, signatures, and ingredients. If the client requires C2PA, use a current C2PA-compatible toolchain and verify that credentials survive the export and platform upload path. If platforms strip metadata, preserve the signed source/output plus a separate ledger in handoff. Do not imply that C2PA proves truthfulness; it records provenance claims whose trust depends on the signer and chain.
Escalate to client, counsel, or platform owner when:
Run this before final handoff:
10. Encoding review: final file opens in target players; codec/container/audio settings match delivery requirements.
11. Provenance review: ledger, licenses, approvals, versions, prompts/seeds, and source notes are complete.
12. Handoff review: final files, sidecars, stems, source package, render notes, and residual risks are included.
Deliver:
The handoff should let another agent or engineer reproduce the render without guessing which assets, props, or settings were used.
User request: "Make a 60-second vertical explainer from generated images, narration, music, captions, and a CTA."
Strong approach:
1080x1920, 30 fps, 60 seconds, language, caption style, brand colors, and music source.public/assets/image/ and narration/music under public/assets/audio/.project.json with five scenes, each with duration, image asset ID, narration segment, headline, caption cue range, and transition style.VerticalExplainer with width={1080}, height={1920}, fps={30}, and durationInFrames derived from scenes.<Series> for scene order and <Sequence> for overlays, audio cues, and caption layers.OffthreadVideo when its FFmpeg-backed extraction is the better fit; otherwise use Img for stills with deterministic camera moves.Example scene data:
{
"id": "scene-03-proof",
"frames": 360,
"image": "workflow-diagram-01",
"narration": "vo-03-proof",
"headline": "The hidden cost is handoff friction",
"captionCueIds": ["c011", "c012", "c013"],
"motion": {"type": "slow-push", "intensity": 0.18},
"transitionOut": "match-cut-line"
}
Example implementation sketch:
const Scene = ({scene}: {scene: SceneData}) => {
const frame = useCurrentFrame();
const {fps, width, height} = useVideoConfig();
const safe = makeSafeArea(width, height);
const reveal = spring({frame, fps, config: {damping: 18}});
return (
<AbsoluteFill style={{backgroundColor: '#080A12'}}>
<KenBurnsImage assetId={scene.image} motion={scene.motion} />
<Headline text={scene.headline} progress={reveal} safe={safe} />
<CaptionLayer cueIds={scene.captionCueIds} safe={safe} />
</AbsoluteFill>
);
};
Why this works: Remotion controls the deterministic timeline, while generated media remains frozen and traceable. The agent can revise copy, captions, timing, or layouts without regenerating all assets.
Likely failures to watch: captions too low for platform UI, images too low-resolution for vertical crop, music masking narration, text expansion in translated variants, and unrecorded AI generation settings.
User request: "Create 40 short product-update videos, one per customer segment, using the same structure but different metrics, screenshots, and CTA."
Strong approach:
VariantProps with schema validation.calculateMetadata() to extend duration for variants with longer localized copy.Example variant prop shape:
const variantSchema = z.object({
variantId: z.string(),
locale: z.string(),
segmentName: z.string(),
metrics: z.array(z.object({
label: z.string(),
value: z.string(),
sourceNote: z.string().optional(),
})),
screenshotAssetId: z.string(),
cta: z.string(),
captionTrack: z.string(),
});
Batch rule: if any variant fails schema, asset existence, caption timing, or safe-area still review, stop the batch and fix the data or layout. Do not render 40 flawed videos faster.
Take calesthio/remotion-video-composition from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.