HyperFrames
HTMLがビデオの唯一の真実源です。コンポジションは、タイミング用のdata-*属性、アニメーション用のGSAPタイムライン、外観用のCSSを持つHTMLファイルです。フレームワークは、クリップの表示/非表示、メディア再生、タイムライン同期を処理します。
Open Designとの統合(このサーフェスにとって重要な要素)
このスキルがOpen Design内で実行される場合(つまり$OD_PROJECT_DIRが設定されている場合)、出力フローは固定されます:レンダリングされた.mp4のみがプロジェクトルートに配置されるべきです。コンポジションのソースファイル(hyperframes.json、meta.json、index.html、アセット)は隠しキャッシュディレクトリ内に配置し、ユーザーのFileViewerやチャットの「生成されたファイル」チップを散らかさないようにします。
OD内でのレンダリングワークフロー — 高速パス:
ほとんどのODリクエスト(「テスト動画」、「5秒の製品紹介」、「デモクリップ」)では、コンポジションHTMLをゼロから作成しないでください。HyperFramesの組み込みスキャフォールドを使用し、プロンプトで実際に変更された箇所のみを編集してください。ゼロから作成するパスは、モデルの出力とチャットツールのサイレント時間に数分かかりますが、スキャフォールドパスは数秒で済みます。
# 1. 隠しキャッシュスロットを選択します。ドットファイルプレフィックスにより、ODのプロジェクトファイル
# リストはそれをスキップするため、ソースファイルがチャットを散らかすことはありません。
COMP_REL=".hyperframes-cache/$(date +%s)-$(openssl rand -hex 2)"
COMP="$OD_PROJECT_DIR/$COMP_REL"
# 2. すぐにレンダリング可能なスキャフォールドを取得します(hyperframes.json、
# meta.json、GSAP CDN + window.__timelines.mainがすでに登録されたindex.html)。
# これはシェル内で実行されます — 純粋なファイルコピーで、Chromeも、npxキャッシュ以外の
# ネットワークも必要ありません。
npx hyperframes init "$COMP" --example blank --skip-skills --non-interactive
# 3. $COMP/index.htmlのみを編集します — デフォルト以外の長さが必要な場合はルートの
# `data-duration`を変更し、<style>内のプレースホルダーパレットを交換し、
# テキスト/画像用に1〜3個のクリップ<div>を追加し、既存の
# `window.__timelines["main"] = gsap.timeline({paused:true})`ブロック内に
# 対応するGSAPトゥイーンを追加します。
# 編集は最小限に抑えます。スキャフォールドはすでに有効なHFです。
# 4. ODデーモンを介してレンダリングをディスパッチします。このシェルから`npx hyperframes
# render`を実行しないでください — デーモンがサンドボックス化されていないプロセスで
# 代わりに実行します。(多くのエージェントCLI、特にClaude Codeは、BashをmacOSの
# sandbox-execでラップしており、その下ではpuppeteerのChromeサブプロセスが
# フレームキャプチャの途中でハングします。デーモンプロセスはサンドボックス化されていないため、
# レンダリングは確実に完了します。)
#
# ディスパッチャーは約1秒以内に{taskId}を返します。
# `"$OD_NODE_BIN" "$OD_BIN" media wait <taskId>`の呼び出しをループして
# レンダリングを完了まで駆動します。
# 各呼び出しは最大25秒間ロングポーリングし(通常のシェルツールのデフォルト30秒制限を十分下回ります)、
# 終了コード0/2/5で完了/実行中/失敗を通知します。
out=$("$OD_NODE_BIN" "$OD_BIN" media generate \
--project "$OD_PROJECT_ID" \
--surface video \
--model hyperframes-html \
--output "<descriptive-name>.mp4" \
--composition-dir "$COMP_REL")
ec=$?
task_id=$(printf '%s\n' "$out" | tail -1 | jq -r '.taskId // empty')
since=$(printf '%s\n' "$out" | tail -1 | jq -r '.nextSince // 0')
while [ "$ec" -eq 2 ] && [ -n "$task_id" ]; do
out=$("$OD_NODE_BIN" "$OD_BIN" media wait "$task_id" --since "$since")
ec=$?
since=$(printf '%s\n' "$out" | tail -1 | jq -r '.nextSince // '"$since")
done
[ "$ec" -ne 0 ] && { echo "$out" >&2; exit "$ec"; }
各generateと各waitの呼び出しは最大約25秒なので、エージェントシェルツールのデフォルト約30秒の制限がトリガーされることはありません。HFからの進行行(Capturing frame N/M)は、ループ全体でライブでstderrにストリーミングされます。レンダリングが完了すると、最後のstdout行は{"file": { "name": "<output>", "size": …, "kind": "video", … }}になります — ユーザーに何が生成されたかを知らせるために、返信内でfile.nameを引用してください。
OD内ではビジュアルアイデンティティゲートをスキップします。 以下の「アプローチ」セクションのHARD-GATEは、DESIGN.mdやvisual-style.mdを読み、スタイルについて3つの質問をしてからコンポジションを作成するよう指示しています。そのゲートはスタンドアロンのHFプロジェクト用です。ODプロジェクトには独自のデザインシステムレイヤーがすでにあります — ユーザーはプロジェクト作成時にビジュアルディレクションを選択済みです。ODのテストレンダリングでは、デフォルトで:暗いキャンバス(#0b0b0f)、1つの暖色アクセント(#ffb76b)、1つの寒色アクセント(#7da4ff)、控えめなモーションを使用します。ユーザーのプロンプトが主題を選ぶには曖昧すぎる場合にのみ、スタイルに関する入力を求めてください(非常に稀です)。
スキャフォールドをスキップしてゼロから作成する場合:ユーザーが明示的に、ブランクテンプレートでは明らかにホストできないもの(例:マルチコンポジションタイムライン、音声連動オーバーレイ、すでに生成したTTSトラックに同期されたキャプション)を要求した場合のみです。それ以外のすべての場合、init + 編集がデフォルトのパスです。
自身のシェルから実行しても安全な、より軽量なHFサブコマンド(Chromeを起動する必要がないもの):
npx hyperframes lint "$COMP"— ディスパッチ前にコンポジションを検証npx hyperframes transcribe <audio>— キャプションを生成npx hyperframes tts <text>— ナレーションを生成
デーモンディスパッチはrender/inspect/preview(Chromeを必要とするもの)のために予約します。
"$OD_NODE_BIN" "$OD_BIN" media generate --model hyperframes-htmlを呼び出さないでください — そのディスパッチャーパスは意図的に400(AGENT_RENDERED)を返します。HyperFramesはあなた自身がnpx経由で直接レンダリングします。
hyperframes.json / meta.json / index.htmlをプロジェクトルートに置かないでください — ODのファイル一覧は再帰的にスキャンするため、ユーザーはチャットに関連のない3つのファイルが表示されているのを見ることになります。
render以外のCLIオプション(lint、preview、transcribe、tts、inspect、benchmark)については、タスクに適している場合(例:コンポジションから参照する前にキャッシュにTTSオーディオを生成する)、シェルツールから直接呼び出してください。
アプローチ
HTMLを書く前に、高いレベルで考えてください:
- 何を — 視聴者は何を体験すべきか? 物語の流れ、重要な瞬間、感情的なビートを特定してください。
- 構造 — コンポジションはいくつか、サブコンポジションかインラインか、どのトラックが何を運ぶか(ビデオ、オーディオ、オーバーレイ、キャプション)。
- タイミング — どのクリップが長さを決めるか、トランジションはどこに配置されるか、ペースはどうか。
- レイアウト — 最初にエンドステートを構築します。「アニメーションの前のレイアウト」を参照してください。
- アニメート — 次に、以下のルールを使用してモーションを追加します。
小さな編集(色の修正、タイミングの調整、1つの要素の追加)の場合は、ルールに直接進んでください。
ビジュアルアイデンティティゲート
以下の順序で確認してください:
- プロジェクトにDESIGN.mdが存在するか? → それを読んでください。その正確な色、フォント、モーションルール、「やってはいけないこと」の制約を使用します。
- visual-style.mdが存在するか? → それを読んでください。その
style_prompt_fullと構造化フィールドを適用します。(注:visual-style.mdはプロジェクト固有のファイルです。visual-styles.mdは8つの名前付きプリセットを持つスタイルライブラリです — 異なるファイルです。) - ユーザーがスタイルを指定したか(例:「スイスパルス」、「ダークでテクニカル」、「高級ブランド」)? → 8つの名前付きプリセットについてvisual-styles.mdを読んでください。最小限のDESIGN.mdを生成してください:
## Style Prompt(1段落)、## Colors(3〜5つのHEX値と役割)、## Typography(1〜2フォントファミリー)、## What NOT to Do(3〜5のアンチパターン)。 - 上記のいずれでもないか? → HTMLを書く前に3つの質問をしてください:
- ムードは?(爆発的 / シネマティック / 流動的 / テクニカル / カオス的 / 暖かみのある)
- 明るいキャンバスか暗いキャンバスか?
- 特定のブランドカラー、フォント、ビジュアルリファレンスはありますか? その後、回答から最小限のDESIGN.mdを生成します。
すべてのコンポジションは、そのパレットとタイポグラフィをDESIGN.md、visual-style.md、または明示的なユーザー指示に遡って追跡できる必要があります。#333、#3b82f6、Robotoに手を伸ばしている場合 — このステップをスキップしています。
モーションのデフォルト、サイズ調整、エントランスパターン、イージングについては、house-style.mdに従ってください。ハウススタイルはHOW(どのように動くか)を扱います。DESIGN.mdはWHAT(どのように見えるか)を扱います。
アニメーションの前のレイアウト
すべての要素を、最も見える瞬間 — 完全に登場し、正しく配置され、まだ退出していないフレーム — に配置すべき場所に配置します。これを最初に静的HTML+CSSとして記述します。まだGSAPは使いません。
これが重要な理由: 要素をアニメーションの開始状態(画面外、スケール0、不透明度0)に配置し、そこから着地すべき位置にトゥイーンすると、最終的なレイアウトを推測していることになります。重なりはビデオがレンダリングされるまで見えません。最初にエンドステートを構築することで、モーションを追加する前にレイアウトの問題を確認して修正できます。
プロセス
- 各シーンのヒーローフレームを特定します — 最も多くの要素が同時に表示される瞬間。これが構築するレイアウトです。
- そのフレームの静的CSSを記述します。
.scene-contentコンテナは、width: 100%; height: 100%; padding: Npx;とdisplay: flex; flex-direction: column; gap: Npx; box-sizing: border-boxを使用して、シーン全体を埋める必要があります。パディングを使用してコンテンツを内側に押し込みます — コンテンツコンテナにposition: absolute; top: Npxを絶対に使用しないでください。絶対配置されたコンテンツコンテナは、コンテンツが残りのスペースより高い場合にあふれます。position: absoluteは装飾要素用にのみ予約してください。 gsap.from()でエントランスを追加 — 画面外/非表示からCSS位置に向かってアニメーションします。CSS位置がグラウンドトゥルースであり、トゥイーンはそこに到達するまでの経路を記述します。gsap.to()でイグジットを追加 — CSS位置から画面外/非表示に向かってアニメーションします。
例
/* scene-contentはシーンを埋め、パディングがコンテンツを配置 */
.scene-content {
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
height: 100%;
padding: 120px 160px;
gap: 24px;
box-sizing: border-box;
}
.title {
font-size: 120px;
}
.subtitle {
font-size: 42px;
}
/* コンテナは任意のシーンサイズ(1920x1080、1080x1920など)を埋めます。
パディングがコンテンツを配置します。Flex + gapが間隔を処理します。 */
間違い — ハードコードされた寸法と絶対配置:
.scene-content {
position: absolute;
top: 200px;
left: 160px;
width: 1920px;
height: 1080px;
display: flex; /* ... */
}
// ステップ3:それらの位置に向かってアニメーション
lt.from(".title", { y: 60, opacity: 0, duration: 0.6, ease: "power3.out" }, 0);
tl.from(".subtitle", { y: 40, opacity: 0, duration: 0.5, ease: "power3.out" }, 0.2);
tl.from(".logo", { scale: 0.8, opacity: 0, duration: 0.4, ease: "power2.out" }, 0.3);
// ステップ4:それらの位置から外にアニメーション
tl.to(".title", { y: -40, opacity: 0, duration: 0.4, ease: "power2.in" }, 3);
tl.to(".subtitle", { y: -30, opacity: 0, duration: 0.3, ease: "power2.in" }, 3.1);
tl.to(".logo", { scale: 0.9, opacity: 0, duration: 0.3, ease: "power2.in" }, 3.2);
要素が時間を超えてスペースを共有する場合
要素Aが要素Bが同じエリアに入る前に退出する場合、両方ともそれぞれのヒーローフレームに対して正しいCSS位置を持つ必要があります。タイムラインの順序付けにより、視覚的に共存することはありませんが、レイアウトステップをスキップすると、タイミングエラーにより誤って重なるケースを見逃します。
意図的な重なりと見なされるもの
レイヤードエフェクト(テキストの後ろのグロー、シャドウ要素、背景パターン)とzスタックデザイン(カードスタック、デプスレイヤー)は意図的なものです。レイアウトステップは、意図しない重なりをキャッチするためのものです — 2つの見出しが互いに重なる、統計がラベルを覆う、コンテンツがフレームからはみ出すなど。
データ属性
すべてのクリップ
| 属性 | 必須 | 値 |
|---|---|---|
id | はい | 一意の識別子 |
data-start | はい | 秒数またはクリップID参照("el-1"、"intro + 2") |
data-duration | img/div/コンポジションには必須 | 秒数。ビデオ/オーディオはメディアの長さをデフォルトにします。 |
data-track-index | はい | 整数。同じトラックのクリップは重複できません。 |
data-media-start | いいえ | ソースへのトリムオフセット(秒数) |
data-volume | いいえ | 0〜1(デフォルトは1) |
data-track-indexは視覚的なレイヤリングに影響しません — CSSのz-indexを使用してください。
コンポジションクリップ
| 属性 | 必須 | 値 |
|---|---|---|
data-composition-id | はい | 一意のコンポジションID |
data-start | はい | 開始時間(ルートコンポジション:"0"を使用) |
data-duration | はい | GSAPタイムラインの長さよりも優先されます |
data-width / data-height | はい | ピクセル寸法(1920x1080 または 1080x1920) |
data-composition-src | いいえ | 外部HTMLファイルへのパス |
コンポジション構造
data-composition-srcを介して読み込まれるサブコンポジションは、<template>ラッパーを使用します。スタンドアロンコンポジション(メインのindex.html)は<template>を使用しません — data-composition-id divを直接<body>に配置します。スタンドアロンファイルに<template>を使用すると、すべてのコンテンツがブラウザから隠され、レンダリングが破損します。
サブコンポジション構造:
<template id="my-comp-template">
<div data-composition-id="my-comp" data-width="1920" data-height="1080">
<!-- コンテンツ -->
<style>
[data-composition-id="my-comp"] {
/* スコープされたスタイル */
}
</style>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<script>
window.__timelines = window.__timelines || {};
const tl = gsap.timeline({ paused: true });
// トゥイーン...
window.__timelines["my-comp"] = tl;
</script>
</div>
</template>
ルートでの読み込み:<div id="el-1" data-composition-id="my-comp" data-composition-src="compositions/my-comp.html" data-start="0" data-duration="10"></div>
タイムライン管理
window.__timelines名前空間を使用して、すべてのタイムラインを保存します。 コンポジションごとに。ラッパー<script>ブロック内で、window.__timelinesが存在することを確認してから、GSAPタイムラインを作成し、割り当てます。window.__timelines["main"]はメイン(ルート)タイムラインを格納します。サブコンポジションは、独自のスクリプトブロック内でwindow.__timelines["comp-id"]に格納します。
絶対に以下のことをしないでください: GSAPタイムラインのアニメーションにtl.play()またはtl.reverse()を呼び出す。フレームワークはこれらを自動的に処理します(コンポジションのdata-durationに従って)。
絶対に以下のことをしないでください: GSAPタイムラインのアニメーションにtl.play()またはtl.reverse()を呼び出す。フレームワークは、少なくとも1回はそれらを自動的に呼び出します。
絶対に以下のことをしないでください: GSAPタイムラインのアニメーションにtl.play()またはtl.reverse()を呼び出す。フレームワークはこれらを処理します。
絶対に以下のことをしないでください: GSAPタイムラインを使用する代わりにrequestAnimationFrameを使用する。フレームワークのレンダラーは、animationstart / animationendイベントに対してGSAPアニメーションをサブスクライブしており、CSSアニメーションやrequestAnimationFrameループは認識しません。
絶対に以下のことをしないでください: data-durationを設定せずにすべてのアニメーションを自動的に完了させようとする。これはduration: "auto"を設定するのと同じであり、おそらく期待どおりには機能しません。
絶対に以下のことをしないでください: 特に最新のスタンドアロンコンポジションで。
代わりに、以下のことを行ってください:
- すべてのアニメーションを一度に再生する場合は、
window.__timelines["main"]をgsap.timeline({paused: true})として作成します。 - サブコンポジションの場合:
window.__timelines["my-comp"]をgsap.timeline({paused: true})として作成します。 - ルートコンポジションのメインスクリプトブロックは、
window.__timelines["main"]を作成し、メイントゥイーンを設定します。 - サブコンポジションは、独自のスクリプトブロック内で
window.__timelines["comp-id"]を作成します。
メイン(ルート)タイムラインは、data-durationとそのタイムライン上のクリップによって駆動されます。フレームワークはこれを自動的に再生します。<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>を各コンポジションに含めてください。
常にpaused: trueを使用してください: gsap.timeline({paused: true})。これにより、フレームワークが再生を制御できるようになります。
絶対に以下のことをしないでください: 他の方法で作成する。
適切な設定の例:
window.__timelines = window.__timelines || {};
const tl = gsap.timeline({paused: true});
tl.to(".box", { x: 100, duration: 1 });
tl.to(".box", { y: 100, duration: 1 });
tl.to(".box", { opacity: 0, duration: 0.5 });
window.__timelines["main"] = tl;
window.__timelinesの外側で作成されたタイムラインは、フレームワークによって無視されます。
paused: trueの重要性: paused: trueを省略すると、タイムラインは即座に再生を開始し、フレームワークがシークして再生制御できなくなります。これにより、レンダリング時に表示/非表示の競合状態が発生します。
window.__timelines["main"]への参照を必ず使用してください: フレームワークはこれを認識します。
window.__timelines["main"]を見つけられない場合、レンダリングは失敗します。
サブコンポジションの場合: フレームワークはwindow.__timelines["<comp-id>"]を探します。<comp-id>はコンポジションのdata-composition-id属性と一致する必要があります。
サブコンポジションスコーピングの例: サブコンポジションのdata-composition-id="my-comp"の場合、タイムラインはwindow.__timelines["my-comp"]に格納する必要があります。
注意: data-composition-id属性は、コンポジションを含む<div>上にある必要があります。<template>上にはありません。
<script>の配置: サブコンポジションの<script>ブロックは、コンポジションの<div>の内側になければなりません。<template>の内側ではありません(ただし、技術的にはテンプレートの内側に配置できますが、スタンドアロンルートファイル内では機能しません)。
<script>の配置(詳細): メイン(ルート)コンポジションでは、<script>ブロックはHTMLの最後(</body>の前)に配置する必要があります。サブコンポジションでは、コンポジションdivの内側に配置する必要があります。
<script>の配置(結論): メインでは、</body>の前に配置します。サブコンポジションでは、コンポジションdivの内側に配置します。
<script>の配置(最終): メインコンポジション:</body>の前。サブコンポジション:コンポジションdivの内側。
すべてのコンポジションで<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>を含めてください。