HyperFrames Core
出力フレームワークはRemotionです。 Compositionは<Composition>に登録されたReactコンポーネントツリーです。現在のフレームが唯一のクロックであり、メディアの再生はDOMではなくRemotionの<Video> / <Audio>コンポーネントに属します。
このスキルはテクニカルコントラクトです。1つのプロジェクトを構築する方法について説明します。以下の本文が構築ガイドです。トピックごとの詳細はreferences/にあり、必要に応じて参照してください。その他の懸念事項は、関連するドメインスキールであるhyperframes-animation、hyperframes-creative、media-use、hyperframes-cli、hyperframes-registryにあります。/hyperframesの機能マップに、各スキルがカバーする内容が記載されています。
移植に関する注意。 このコントラクトは、HTML +
data-*+ 一時停止GSAPタイムラインコントラクトを置き換えるものです。data-composition-id/data-start/data-durationを持つindex.htmlや、window.__timelinesを説明するリファレンスファイルを含むプロジェクトを見つけた場合、それは古いコントラクトです。触る前にreferences/legacy-html-contract.mdをお読みください。
リファレンス
| ファイル | 目的 |
|---|---|
references/minimal-composition.md | 最小のレンダリング可能なプロジェクトスケルトンから始める |
references/project-structure.md | src/のレイアウト、シーンをコンポーネントに分割、モノリシックかモジュールかの決定 |
references/timing.md | 秒をフレームに変換、<Sequence>の配置、<Series> / <TransitionSeries>の使用、シーン間の相対時間設定 |
references/layers-and-order.md | レイヤーのスタック、zオーダーの制御、同じ時間軸上で2つのシーンを重ねる |
references/props-and-schemas.md | Propsの宣言、Zodスキーマの作成、defaultPropsとcalculateMetadataの配線 |
references/media.md | <Video> / <OffthreadVideo> / <Audio> / <Img>の配置、トリミング、ボリューム設定、staticFileの使用 |
references/determinism-rules.md | レンダリングのシーク可能性と再現性の維持、禁止事項、delayRenderによる非同期アセット読み込み |
references/editing-recipes.md | カット/トリム/並べ替え/リタイム/フリーズ/カメラ/マスク/クロスフェード/オーディオのレシピとその制限 |
references/legacy-html-contract.md | 古いHTML + data-*コントラクトを使用しているプロジェクトの認識と移行 |
references/tailwind.md | Tailwind v4プロジェクトでの作業方法 |
references/storyboard-format.md | STORYBOARD.md計画(+解析されたマニフェスト)の作成 |
references/review-loop.md | ライブボード上で計画→スケッチ→構築のレビューパスを実行 |
references/production-loop.md | 承認された計画から配信動画への変換 |
references/brief-contract.md | ブリーフの基本ルール:モードの導出、共有フィールドレジストリ、質問の不変条件 |
references/brief-format.md | BRIEF.mdの作成 |
references/script-format.md | オプションのSCRIPT.md(固定されたナレーション)の作成 |
references/subagent-dispatch.md | サブエージェントディスパッチ動詞をハーネスにマッピング |
references/frame-worker-core.md | 共有フレームワーカーの役割コントラクト |
アニメーションの詳細(スプリング設定、イージング、ランタイムアダプター)についてはhyperframes-animationを参照してください。
Compositionの構築
プロジェクトの構造
package.json # remotion, react, react-dom; studio/render用のスクリプト
remotion.config.ts # レンダリング時設定(コーデック、画像フォーマット、Tailwind/webpackオーバーライド)
tsconfig.json
public/ # staticFile()が解決するすべてのファイル
src/
index.ts # registerRoot(Root)
Root.tsx # すべての<Composition>はここに配置
<Name>/
index.tsx # compositionのトップレベルコンポーネント
scenes/*.tsx # シーンごとに1つのコンポーネント
schema.ts # Zodスキーマ + 推論されたProps型
src/index.tsはエントリーポイントであり、次の1つのことだけを行います:
import { registerRoot } from "remotion";
import { Root } from "./Root";
registerRoot(Root);
Compositionの登録
レンダリング可能なものはすべてRoot.tsx内の<Composition>です。idはCLIが名前でレンダリングするものです。
import { Composition } from "remotion";
import { LaunchVideo } from "./LaunchVideo";
import { launchSchema } from "./LaunchVideo/schema";
const FPS = 30;
export const Root: React.FC = () => (
<>
<Composition
id="launch"
component={LaunchVideo}
durationInFrames={45 * FPS}
fps={FPS}
width={1920}
height={1080}
schema={launchSchema}
defaultProps={{ productName: "Acme", accent: "#5B8CFF" }}
/>
</>
);
durationInFramesはレンダリング長です。秒ではなくフレームであり、これを間違えることは最も一般的な作成ミスです。1350のような生の数値ではなく、名前付きのFPS定数から導出してください。
フレームが唯一のクロック
すべてのアニメーション値はuseCurrentFrame()の純粋関数です。これによりレンダリングがシーク可能になります。レンダラーはフレーム812にコールドスタートし、そのまま再生した場合と同じピクセルを取得する必要があります。
import { AbsoluteFill, useCurrentFrame, useVideoConfig, interpolate, spring } from "remotion";
export const Title: React.FC<{ text: string }> = ({ text }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const enter = spring({ frame, fps, config: { damping: 18 } });
const y = interpolate(enter, [0, 1], [40, 0]);
return (
<AbsoluteFill style={{ justifyContent: "center", alignItems: "center" }}>
<h1 style={{ opacity: enter, transform: `translateY(${y}px)` }}>{text}</h1>
</AbsoluteFill>
);
};
フレーム範囲をマッピングするinterpolateは常にクランプしてください。そうしないと値が範囲を超えて補外され、フェードインが不透明度4になってしまいます:
const opacity = interpolate(frame, [0, 15], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
タイミングは<Sequence>
<Sequence>はその子をタイムシフトします。内部では、useCurrentFrame()はシーケンス自体の開始時に0を返します。このローカルリベーシングこそがポイントであり、シーンコンポーネントはマスタータイムライン上の自身の位置を知る必要がありません。
<AbsoluteFill>
<Sequence durationInFrames={90}>
<Hook />
</Sequence>
<Sequence from={90} durationInFrames={150}>
<Feature />
</Sequence>
<Sequence from={240} durationInFrames={120}>
<Close />
</Sequence>
</AbsoluteFill>
連続するシーンの場合、<Series>がオフセットを計算するため、期間を編集しても後続のすべてのfromを手動で修正する必要はありません:
<Series>
<Series.Sequence durationInFrames={90}><Hook /></Series.Sequence>
<Series.Sequence durationInFrames={150}><Feature /></Series.Sequence>
<Series.Sequence durationInFrames={120}><Close /></Series.Sequence>
</Series>
<Series.Sequence offset={-15}>は、シーンを前倒しして前のシーンと重ねます。これにより計算なしでクロスフェード区間を実現できます。詳細と、プレゼンテーションベースのトランジションのための<TransitionSeries>については、references/timing.mdを参照してください。
レイヤーはトラックを置き換える
トラックインデックスはありません。重ね順は<AbsoluteFill>内のDOM順序です。後ろの兄弟が上に描画されます。ナレーショントラック、BGM、3つのビジュアルシーンは5つの兄弟として配置されます。
<AbsoluteFill>
<Audio src={staticFile("music.mp3")} volume={0.18} />
<Audio src={staticFile("vo.mp3")} />
<Sequence durationInFrames={90}><Hook /></Sequence>
<Sequence from={90}><Feature /></Sequence>
<Overlay /> {/* 上記のすべての上に描画 */}
</AbsoluteFill>
<Sequence layout="none">は、暗黙の<AbsoluteFill>ラッパーを削除します。子が絶対配置ではなく、親のflexやgridレイアウトに参加する必要がある場合に使用します。
メディアはRemotionに属する
生の<video>、<audio>、<img>タグを使用しないでください。Remotionのコンポーネントを使用することで、レンダラーは決定論的にシークおよびフレーム抽出できます。
| ニーズ | 使用 |
|---|---|
| レンダリング内のビデオ | <OffthreadVideo src>(推奨) |
| DOMレベルの制御が必要なビデオ | <Video src> |
| オーディオ | <Audio src volume trimBefore trimAfter> |
| 静止画像 | <Img src> |
| 埋め込みページ | <IFrame src> |
public/以下のファイル | staticFile("dir/name.ext") |
<OffthreadVideo>はブラウザ外でFFmpegを使用してフレームを抽出するため、レンダリングがより高速かつ正確です。実際の要素(例えばcurrentTimeに基づいて何かを駆動する場合など)が必要な場合にのみ<Video>を使用します。ローカルアセットにはstaticFile()が必須です。裸の"/music.mp3"文字列はStudioでは動作してもLambdaレンダリングでは壊れます。トリミング、ボリュームランプ、再生速度についてはreferences/media.mdを参照してください。
Propsとスキーマ
Compositionの入力はReactのPropsであり、Zodスキーマによって型付けされます。これによりStudioが編集可能なコントロールをレンダリングし、不正な--propsペイロードが間違ったレンダリングではなくエラーとして失敗します。
import { z } from "zod";
import { zColor } from "@remotion/zod-types";
export const launchSchema = z.object({
productName: z.string(),
accent: zColor(),
features: z.array(z.object({ title: z.string(), body: z.string() })),
});
export type LaunchProps = z.infer<typeof launchSchema>;
期間がPropsに依存する場合(未知の長さの機能リスト、未知の長さのオーディオファイルなど)、ハードコードされた推測ではなくcalculateMetadataで計算します。references/props-and-schemas.mdを参照してください。
不可譲のルール
型チェックでは検出できないサイレントバグ。完全な根拠はreferences/determinism-rules.mdにあります。
- 壁時計禁止。
Date.now()、new Date()、performance.now()は使用しないでください。時間はuseCurrentFrame()から取得します。 - シードなし乱数禁止。
Math.random()はフレームごとに異なる値を返し、ストロボ効果を引き起こします。Remotionのrandom(seed)を使用してください。同じシード、同じ値、すべてのフレームとすべてのレンダリングで一貫します。 - 状態駆動アニメーション禁止。
useState/useReducer/useEffectはビジュアルを駆動してはいけません。レンダラーはフレームを順不同で並行ワーカーにマウントします。順方向に再生することで蓄積された状態は存在しません。代わりにframeから導出してください。 - CSSアニメーション、トランジション、
requestAnimationFrame禁止。 これらはブラウザ自身のクロックで動作し、レンダラーはそれを進めません。すべて最初のフレームでフリーズした状態でレンダリングされます。 - 非同期処理は
delayRenderを使用。 フェッチ、フォント、レイアウト測定:前にdelayRender()を呼び出し、後にcontinueRender(handle)を呼び出さないと、処理が完了する前にフレームがキャプチャされます。 - 補間をクランプする。 クランプされていない
interpolateは入力範囲外で補外されます。 - アニメーションするものには絶対配置を使用。 ノーマルフロー内で変換された要素は、移動するたびに兄弟要素を引きずります。
既存のCompositionの編集
- まずファイルを読んでください。無関係なタイミング、Props、ID、アセットパスは保持します。
- 既存の
Compositionのidに合わせてください。CLI、レンダリングスクリプト、CIはすべてこれでCompositionを識別します。名前の変更は破壊的変更です。 - シーンの追加:オフセットが自動計算されるため、
<Series>を推奨します。手動オフセットの<Sequence>リストでは、後続のすべてのfromを修正してください。 - 期間の変更:
<Composition>のdurationInFramesも確認してください。Compositionの長さを超えて実行されるシーンは、静かに切り詰められます。 - コンポーネントの追加:Propsと
frameの純粋関数として維持してください。
検証
コマンドの詳細はhyperframes-cliにあります。
-
npx tsc --noEmit、スキーマとProps型が実際に一致していることを確認 -
npx remotion still <id> out/probe.png --frame <n>いくつかのシーン中間点で実行し、それぞれを確認 -
npx remotion studioでレビュー、ユーザーがスクラブしてPropsを自分で調整できるようにする - ユーザーが承認した場合のみ
npx remotion render <id>