---
name: remotion-composition
description: Generates Remotion composition structure focusing ONLY on Sequence ordering, scene transitions, and duration mapping. Input is scene list with durations. Output is COMPOSITION_STRUCTURE.md with Sequence layout and timing calculations. Use when organizing scenes or when asked to "structure composition", "layout scenes", "calculate timing".
---
# Remotion Composition
Generates composition structure documents that define how scenes are ordered, timed, and transitioned in a Remotion video. This skill focuses exclusively on Sequence layout and timing orchestration.
## What This Skill Does
Generates composition structure for:
1. **Sequence layout** — Ordering and positioning of scene Sequences
2. **Timing calculations** — Start frames, end frames, duration for each scene
3. **Scene transitions** — Overlap and crossfade timing between scenes
4. **Duration mapping** — Converting seconds to frames for all scenes
5. **Timing constants** — Structured timing object for constants.ts
## Scope Boundaries
**IN SCOPE:**
- Sequence component organization
- Frame calculations for scene timing
- Scene overlap and transition timing
- Duration constant generation
- Scene ordering logic
**OUT OF SCOPE:**
- Scene component implementation (use `/remotion-component-gen`)
- Animation parameters (use `/remotion-animation`)
- Visual styling (colors, layouts)
- Asset management (use `/remotion-asset-coordinator`)
## Input/Output Formats
### Input Format: Scene List with Durations
Accepts scene timing specifications:
**Natural Language:**
```
Scene 1 (Intro): 0-5 seconds
Scene 2 (Features): 5-15 seconds
Scene 3 (Demo): 15-25 seconds
Scene 4 (CTA): 25-30 seconds
```
**Structured Format:**
```markdown
## Scene Timing
**Total Duration:** 30 seconds
**Frame Rate:** 30 fps
**Total Frames:** 900
**Scenes:**
1. Scene 1 - Intro: 5 seconds (0s - 5s)
2. Scene 2 - Features: 10 seconds (5s - 15s)
3. Scene 3 - Demo: 10 seconds (15s - 25s)
4. Scene 4 - CTA: 5 seconds (25s - 30s)
**Transitions:**
- Fade transition between scenes: 0.5 seconds (15 frames)
```
### Output Format: COMPOSITION_STRUCTURE.md
Generates composition structure document:
```markdown
# Composition Structure: ProductDemo
## Status
✅ Sequence layout defined
✅ Timing calculations complete
⏳ Ready for scene implementation
## Composition Overview
**Total Duration:** 30 seconds (900 frames @ 30fps)
**Scenes:** 4
**Transitions:** Crossfade (15 frames)
## Scene Timing Constants
```typescript
const FPS = 30;
export const SCENE_TIMING = {
intro: {
start: 0,
end: 150,
duration: 150,
// 0s - 5s
},
features: {
start: 150,
end: 450,
duration: 300,
// 5s - 15s
},
demo: {
start: 450,
end: 750,
duration: 300,
// 15s - 25s
},
cta: {
start: 750,
end: 900,
duration: 150,
// 25s - 30s
},
} as const;
// Transition timing
export const TRANSITIONS = {
crossfadeDuration: 15, // frames (0.5 seconds)
} as const;
```
## Composition Layout
Main composition with Sequence structure:
```typescript
import { AbsoluteFill, Sequence } from "remotion";
import { SCENE_TIMING } from "./constants";
import { Scene1Intro } from "./scenes/Scene1Intro";
import { Scene2Features } from "./scenes/Scene2Features";
import { Scene3Demo } from "./scenes/Scene3Demo";
import { Scene4CTA } from "./scenes/Scene4CTA";
export function ProductDemo() {
return (
{/* Scene 1: Intro (0s - 5s) */}
{/* Scene 2: Features (5s - 15s) */}
{/* Scene 3: Demo (15s - 25s) */}
{/* Scene 4: CTA (25s - 30s) */}
);
}
```
## Scene Timing Breakdown
| Scene | Name | Duration | Frames | Start Frame | End Frame |
|-------|------|----------|--------|-------------|-----------|
| 1 | Intro | 5s | 150 | 0 | 150 |
| 2 | Features | 10s | 300 | 150 | 450 |
| 3 | Demo | 10s | 300 | 450 | 750 |
| 4 | CTA | 5s | 150 | 750 | 900 |
**Total:** 30 seconds (900 frames)
## Timeline Visualization
```
Frame: 0 150 450 750 900
Time: 0s 5s 15s 25s 30s
|---------|---------|---------|---------|
Scene: | Intro | Features| Demo | CTA |
|---------|---------|---------|---------|
```
## Next Steps
1. **Implement scenes** via `/remotion-component-gen`
2. **Add transitions** if needed (crossfades, wipes)
3. **Integrate constants** into composition constants.ts
4. **Test timing** in Remotion preview
5. **Adjust durations** if scenes feel too fast/slow
## Checklist
- [x] Scene timing calculated
- [x] Sequence layout defined
- [x] Constants generated
- [x] Timing constants structured
- [ ] Scene components implemented (next step)
- [ ] Transitions added (if needed)
- [ ] Timing tested in preview
```
## Composition Patterns
### Pattern 1: Sequential Scenes (No Overlap)
Standard sequential layout where scenes don't overlap:
```typescript
```
### Pattern 2: Overlapping Scenes (Crossfade)
Scenes overlap for smooth transitions:
```typescript
const CROSSFADE = 15; // frames
// Scene 1: Full duration
// Scene 2: Starts before Scene 1 ends
// Scene 3: Starts before Scene 2 ends
```
### Pattern 3: Layered Composition
Background + foreground scenes running simultaneously:
```typescript
{/* Background layer - runs full duration */}
{/* Foreground scenes - sequential */}
```
### Pattern 4: Nested Sequences
Sub-scenes within main scenes:
```typescript
{/* Sub-scene 1 */}
{/* Sub-scene 2 */}
```
## Timing Calculation Helpers
Common frame calculations:
```typescript
// Convert seconds to frames
const secondsToFrames = (seconds: number, fps: number = 30): number =>
Math.round(seconds * fps);
// Calculate scene timing
interface SceneTiming {
start: number;
end: number;
duration: number;
}
const calculateSceneTiming = (
startSeconds: number,
durationSeconds: number,
fps: number = 30
): SceneTiming => {
const start = secondsToFrames(startSeconds, fps);
const duration = secondsToFrames(durationSeconds, fps);
const end = start + duration;
return { start, end, duration };
};
// Calculate crossfade overlap
const calculateCrossfade = (
scene1Start: number,
scene1Duration: number,
crossfadeDuration: number
) => ({
scene1: {
from: scene1Start,
durationInFrames: scene1Duration,
},
scene2: {
from: scene1Start + scene1Duration - crossfadeDuration,
durationInFrames: crossfadeDuration, // or more if scene is longer
},
});
// Validate total duration
const validateDuration = (
scenes: SceneTiming[],
expectedTotal: number
): boolean => {
const lastScene = scenes[scenes.length - 1];
return lastScene.end === expectedTotal;
};
```
## Scene Timing Generation
Automated timing generation from scene list:
```typescript
interface SceneSpec {
name: string;
durationSeconds: number;
}
const generateSceneTiming = (
scenes: SceneSpec[],
fps: number = 30
) => {
let currentFrame = 0;
const timing: Record = {};
for (const scene of scenes) {
const duration = secondsToFrames(scene.durationSeconds, fps);
timing[scene.name] = {
start: currentFrame,
end: currentFrame + duration,
duration,
};
currentFrame += duration;
}
return {
timing,
totalFrames: currentFrame,
totalSeconds: currentFrame / fps,
};
};
// Usage:
const scenes = [
{ name: 'intro', durationSeconds: 5 },
{ name: 'features', durationSeconds: 10 },
{ name: 'demo', durationSeconds: 10 },
{ name: 'cta', durationSeconds: 5 },
];
const result = generateSceneTiming(scenes, 30);
// Result:
// {
// timing: {
// intro: { start: 0, end: 150, duration: 150 },
// features: { start: 150, end: 450, duration: 300 },
// ...
// },
// totalFrames: 900,
// totalSeconds: 30,
// }
```
## Transition Patterns
### Crossfade Transition
Smooth opacity crossfade between scenes:
```typescript
const CROSSFADE = 15;
// Scene 1 - fades out at end
// Scene 2 - fades in at start
// In Scene component:
function Scene1({ crossfadeOut = 0 }) {
const frame = useCurrentFrame();
const { durationInFrames } = useVideoConfig();
const opacity = crossfadeOut > 0
? interpolate(
frame,
[durationInFrames - crossfadeOut, durationInFrames],
[1, 0],
{ extrapolateRight: 'clamp' }
)
: 1;
return ...;
}
```
### Hard Cut Transition
No transition, instant scene change:
```typescript
```
### Slide Transition
One scene slides out while next slides in:
```typescript
const TRANSITION_DURATION = 20;
```
## Duration Validation
Ensuring timing adds up correctly:
```typescript
// Validation helper
const validateCompositionTiming = (
scenes: Record,
expectedDuration: number,
fps: number
): { valid: boolean; issues: string[] } => {
const issues: string[] = [];
// Check for gaps
const sceneList = Object.entries(scenes).sort((a, b) => a[1].start - b[1].start);
for (let i = 0; i < sceneList.length - 1; i++) {
const currentEnd = sceneList[i][1].end;
const nextStart = sceneList[i + 1][1].start;
if (nextStart > currentEnd) {
issues.push(`Gap detected: ${currentEnd} to ${nextStart} (${(nextStart - currentEnd) / fps}s)`);
}
if (nextStart < currentEnd) {
issues.push(`Overlap detected: ${sceneList[i][0]} and ${sceneList[i + 1][0]}`);
}
}
// Check total duration
const lastScene = sceneList[sceneList.length - 1][1];
if (lastScene.end !== expectedDuration) {
issues.push(
`Total duration mismatch: expected ${expectedDuration}, got ${lastScene.end} (${lastScene.end / fps}s)`
);
}
return {
valid: issues.length === 0,
issues,
};
};
```
## Timeline Visualization Helper
Generate ASCII timeline:
```typescript
const generateTimeline = (
scenes: Record,
fps: number,
width: number = 60
) => {
const lastScene = Object.values(scenes).reduce((max, scene) =>
scene.end > max ? scene.end : max, 0
);
const timeline: string[] = [];
// Frame markers
const frameMarkers = Array.from({ length: width + 1 }, (_, i) => {
const frame = Math.round((i / width) * lastScene);
return frame.toString().padStart(4);
}).join('');
timeline.push('Frame: ' + frameMarkers);
// Time markers
const timeMarkers = Array.from({ length: width + 1 }, (_, i) => {
const time = ((i / width) * lastScene) / fps;
return time.toFixed(1) + 's';
}).join(' ');
timeline.push('Time: ' + timeMarkers);
// Scene bars
for (const [name, timing] of Object.entries(scenes)) {
const startPos = Math.round((timing.start / lastScene) * width);
const endPos = Math.round((timing.end / lastScene) * width);
const bar = ' '.repeat(startPos) + '|' + '='.repeat(endPos - startPos - 1) + '|';
timeline.push(`${name.padEnd(8)}: ${bar}`);
}
return timeline.join('\n');
};
```
## Best Practices
### Timing Guidelines
```typescript
// Minimum scene duration for readability
const MIN_SCENE_DURATION = 30; // 1 second at 30fps
// Standard transition duration
const STANDARD_TRANSITION = 15; // 0.5 seconds
// Maximum scene duration before pacing feels slow
const MAX_SCENE_DURATION = 600; // 20 seconds
// Recommended scene duration range
const IDEAL_SCENE_DURATION = {
min: 60, // 2 seconds
max: 300, // 10 seconds
};
```
### Composition Organization
```typescript
// Group related Sequences
// Good:
<>
{/* Background layer */}
{/* Content scenes */}
>
// Bad: Mixed layers without organization
```
## Integration Workflow
1. **Define scene durations** → Input to this skill
2. **Generate composition structure** → COMPOSITION_STRUCTURE.md
3. **Add to composition file** (index.tsx)
4. **Add timing to constants** (constants.ts)
5. **Implement scenes** via `/remotion-component-gen`
6. **Test timing** in preview
7. **Adjust if needed** and regenerate
## Integration with Other Skills
This skill coordinates with:
```
remotion-composition (this skill)
↓ outputs: COMPOSITION_STRUCTURE.md
remotion-component-gen
↓ implements scenes with timing awareness
remotion-animation
↓ animation timing works within scene durations
```
**Works with:**
- `/motion-designer` — Scene timing from design specs
- `/remotion-scaffold` — Structure added to composition file
- `/remotion-animation` — Timing coordinates with animation configs
- `/remotion-component-gen` — Scenes fit within calculated durations
- `/remotion-spec-translator` — Orchestrates this skill in pipeline
---
This skill provides precise composition structure and timing calculations that ensure smooth, well-paced Remotion videos.