mcpField
R
remotion-composition

remotion-composition

github.com/ncklrsクリエイティブ・メディア53196 回インストール35 週間前に更新
リポジトリ

remotion-composition について

シーン一覧と長さをもとに、Remotion動画の構成を計画するスキルです。Sequenceの配置、フレーム計算、トランジション、時間定数を扱い、シーン実装やビジュアルスタイルは対象外です。

主な機能

  1. 01Sequenceの順序と配置
  2. 02シーンの開始・終了・長さの計算
  3. 03重なりとトランジションの時間設定
  4. 04秒からフレームへの変換
  5. 05時間定数と構成ドキュメントの生成

活用シーン

  1. 01シーンの長さから構成タイムラインを作成
  2. 02シーン間のクロスフェードを計画
  3. 03Remotionプロジェクト用の時間定数を生成
  4. 04構成全体の長さを確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/ncklrs/startup-os-skills --skill remotion-composition

インストール後、Claude Code で/remotion-compositionを入力して読み込まれたか確認します。

詳細

Remotion Composition

Remotion の動画でシーンの順序、タイミング、トランジションを定義するコンポジション構造ドキュメントを生成します。このスキルは、Sequence のレイアウトとタイミングのオーケストレーションに特化しています。

このスキルでできること

以下のコンポジション構造を生成します。

  1. Sequence のレイアウト — シーンの Sequence の順序と配置
  2. タイミングの計算 — 各シーンの開始フレーム、終了フレーム、再生時間
  3. シーンのトランジション — シーン間のオーバーラップとクロスフェードのタイミング
  4. 再生時間の変換 — すべてのシーンの秒数をフレーム数に変換
  5. タイミング定数 — constants.ts 用の構造化されたタイミングオブジェクト

対象範囲

対象:

  • Sequence コンポーネントの構成
  • シーンのタイミングに関するフレーム計算
  • シーンのオーバーラップとトランジションのタイミング
  • 再生時間定数の生成
  • シーンの順序を決めるロジック

対象外:

  • シーンコンポーネントの実装(/remotion-component-gen を使用)
  • アニメーションパラメーター(/remotion-animation を使用)
  • ビジュアルスタイル(色、レイアウト)
  • アセット管理(/remotion-asset-coordinator を使用)

入出力形式

入力形式:再生時間付きのシーンリスト

シーンのタイミング指定を受け付けます。

自然言語:

Scene 1 (Intro): 0-5 seconds
Scene 2 (Features): 5-15 seconds
Scene 3 (Demo): 15-25 seconds
Scene 4 (CTA): 25-30 seconds

構造化形式:

## 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)
出力形式:COMPOSITION_STRUCTURE.md

コンポジション構造ドキュメントを生成します。

# 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;

コンポジションのレイアウト

Sequence 構造を含むメインコンポジション:

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 (
    <AbsoluteFill>
      {/* Scene 1: Intro (0s - 5s) */}
      <Sequence
        from={SCENE_TIMING.intro.start}
        durationInFrames={SCENE_TIMING.intro.duration}
      >
        <Scene1Intro />
      </Sequence>

      {/* Scene 2: Features (5s - 15s) */}
      <Sequence
        from={SCENE_TIMING.features.start}
        durationInFrames={SCENE_TIMING.features.duration}
      >
        <Scene2Features />
      </Sequence>

      {/* Scene 3: Demo (15s - 25s) */}
      <Sequence
        from={SCENE_TIMING.demo.start}
        durationInFrames={SCENE_TIMING.demo.duration}
      >
        <Scene3Demo />
      </Sequence>

      {/* Scene 4: CTA (25s - 30s) */}
      <Sequence
        from={SCENE_TIMING.cta.start}
        durationInFrames={SCENE_TIMING.cta.duration}
      >
        <Scene4CTA />
      </Sequence>
    </AbsoluteFill>
  );
}

シーンのタイミング内訳

シーン名前再生時間フレーム数開始フレーム終了フレーム
1Intro5s1500150
2Features10s300150450
3Demo10s300450750
4CTA5s150750900

合計: 30 秒(900 フレーム)

タイムラインの可視化

Frame:     0        150       450       750       900
Time:      0s        5s        15s       25s       30s
           |---------|---------|---------|---------|
Scene:     |  Intro  | Features|   Demo  |   CTA   |
           |---------|---------|---------|---------|

次のステップ

  1. シーンを実装する(/remotion-component-gen を使用)
  2. 必要に応じてトランジションを追加する(クロスフェード、ワイプ)
  3. 定数をコンポジションの constants.ts に統合する
  4. Remotion のプレビューでタイミングをテストする
  5. シーンの再生が速すぎる/遅すぎる場合は、再生時間を調整する

チェックリスト

  • シーンのタイミングを計算済み
  • Sequence のレイアウトを定義済み
  • 定数を生成済み
  • タイミング定数を構造化済み
  • シーンコンポーネントを実装済み(次のステップ)
  • トランジションを追加済み(必要な場合)
  • プレビューでタイミングをテスト済み

## Composition Patterns

### Pattern 1: Sequential Scenes (No Overlap)

Standard sequential layout where scenes don't overlap:

```typescript
<Sequence from={0} durationInFrames={150}>
  <Scene1 />
</Sequence>

<Sequence from={150} durationInFrames={300}>
  <Scene2 />
</Sequence>

<Sequence from={450} durationInFrames={300}>
  <Scene3 />
</Sequence>
パターン 2:シーンのオーバーラップ(クロスフェード)

シーンをオーバーラップさせて、滑らかなトランジションを実現します。

const CROSSFADE = 15; // frames

// Scene 1: Full duration
<Sequence from={0} durationInFrames={150}>
  <Scene1 />
</Sequence>

// Scene 2: Starts before Scene 1 ends
<Sequence from={150 - CROSSFADE} durationInFrames={300 + CROSSFADE}>
  <Scene2 />
</Sequence>

// Scene 3: Starts before Scene 2 ends
<Sequence from={450 - CROSSFADE} durationInFrames={300 + CROSSFADE}>
  <Scene3 />
</Sequence>
パターン 3:レイヤー化されたコンポジション

背景と前景のシーンを同時に再生します。

{/* Background layer - runs full duration */}
<Sequence from={0} durationInFrames={900}>
  <BackgroundScene />
</Sequence>

{/* Foreground scenes - sequential */}
<Sequence from={0} durationInFrames={150}>
  <Scene1 />
</Sequence>

<Sequence from={150} durationInFrames={300}>
  <Scene2 />
</Sequence>
パターン 4:ネストされた Sequence

メインシーン内にサブシーンを配置します。

<Sequence from={0} durationInFrames={300}>
  <AbsoluteFill>
    {/* Sub-scene 1 */}
    <Sequence from={0} durationInFrames={100}>
      <Intro />
    </Sequence>

    {/* Sub-scene 2 */}
    <Sequence from={100} durationInFrames={200}>
      <MainContent />
    </Sequence>
  </AbsoluteFill>
</Sequence>

タイミング計算ヘルパー

一般的なフレーム計算:

// 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;
};

シーンのタイミング生成

シーンリストからタイミングを自動生成します。

interface SceneSpec {
  name: string;
  durationSeconds: number;
}

const generateSceneTiming = (
  scenes: SceneSpec[],
  fps: number = 30
) => {
  let currentFrame = 0;
  const timing: Record<string, SceneTiming> = {};

  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,
// }

トランジションのパターン

クロスフェードトランジション

シーン間で不透明度を滑らかにクロスフェードします。

const CROSSFADE = 15;

// Scene 1 - fades out at end
<Sequence from={0} durationInFrames={150}>
  <Scene1 crossfadeOut={CROSSFADE} />
</Sequence>

// Scene 2 - fades in at start
<Sequence from={150 - CROSSFADE} durationInFrames={300}>
  <Scene2 crossfadeIn={CROSSFADE} />
</Sequence>

// 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 <AbsoluteFill style={{ opacity }}>...</AbsoluteFill>;
}
ハードカットトランジション

トランジションを使わず、シーンを瞬時に切り替えます。

<Sequence from={0} durationInFrames={150}>
  <Scene1 />
</Sequence>

<Sequence from={150} durationInFrames={300}>
  <Scene2 />
</Sequence>
スライドトランジション

次のシーンがスライドインする間に、現在のシーンがスライドアウトします。

const TRANSITION_DURATION = 20;

<Sequence from={0} durationInFrames={150}>
  <Scene1 slideOut={TRANSITION_DURATION} />
</Sequence>

<Sequence from={150 - TRANSITION_DURATION} durationInFrames={300}>
  <Scene2 slideIn={TRANSITION_DURATION} />
</Sequence>

再生時間の検証

タイミングの合計が正しいことを確認します。

// Validation helper
const validateCompositionTiming = (
  scenes: Record<string, SceneTiming>,
  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,
  };
};

タイムライン可視化ヘルパー

ASCII タイムラインを生成します。

const generateTimeline = (
  scenes: Record<string, SceneTiming>,
  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');
};

ベストプラクティス

タイミングのガイドライン
// 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
};
コンポジションの構成
// Group related Sequences
// Good:
<>
  {/* Background layer */}
  <Sequence from={0} durationInFrames={900}>
    <Background />
  </Sequence>

  {/* Content scenes */}
  <Sequence from={0} durationInFrames={150}>
    <Scene1 />
  </Sequence>
  <Sequence from={150} durationInFrames={300}>
    <Scene2 />
  </Sequence>
</>

// Bad: Mixed layers without organization

統合ワークフロー

  1. シーンの再生時間を定義する → このスキルへの入力
  2. コンポジション構造を生成する → COMPOSITION_STRUCTURE.md
  3. コンポジションファイルに追加する(index.tsx)
  4. タイミングを定数に追加する(constants.ts)
  5. シーンを実装する(/remotion-component-gen を使用)
  6. プレビューでタイミングをテストする
  7. 必要に応じて調整して再生成する

他のスキルとの連携

このスキルは、以下と連携します。

remotion-composition (this skill)
    ↓ outputs: COMPOSITION_STRUCTURE.md
remotion-component-gen
    ↓ implements scenes with timing awareness
remotion-animation
    ↓ animation timing works within scene durations

連携するスキル:

  • /motion-designer — デザイン仕様に基づくシーンのタイミング
  • /remotion-scaffold — コンポジションファイルへの構造の追加
  • /remotion-animation — アニメーション設定とのタイミング調整
  • /remotion-component-gen — 算出した再生時間に収まるシーン
  • /remotion-spec-translator — パイプライン内でこのスキルを統括

このスキルでは、Remotion 動画を滑らかでテンポよく仕上げるための、正確なコンポジション構造とタイミング計算を提供します。

コメント