D0 HyperFrame 動画制作
HyperFrames HTML + GSAP を使って D0 ブランド動画を制作するための完全なワークフロー。 本ファイルを唯一の権威とします——フォント、カラー、レイアウト、アニメーション、トランジションルールはすべてここで定義します。
フォント仕様は
d0-brandスキルを最終的な権威とし、本スキルは動画制作に関連する追加規定のみを上書きします。
トリガーワード
- "D0 動画を作って" / "d0 hyperframe"
- "hyperframes で動画を作って" / "/d0-hyperframe"
Step 0 — プロジェクトと素材の初期化
推奨:スキル付属の初期化スクリプトを使用する
リポジトリをインストールしたら、本スキル内のスクリプトを実行します。HyperFrames プロジェクトを初期化し、D0 ロゴ、D0 アバター、必要なすべてのフォントをコピーします。
bash "${CODEX_HOME:-$HOME/.codex}/skills/d0-hyperframe/scripts/bootstrap_project.sh" .
プロジェクトがすでに初期化されている場合は、ブランド素材のみを同期します。
bash "${CODEX_HOME:-$HOME/.codex}/skills/d0-hyperframe/scripts/bootstrap_project.sh" . --assets-only
このスクリプトは Node.js 18+ と npx に依存します。HyperFrames CLI は npx hyperframes@latest で必要に応じて実行され、サードパーティのパッケージはスキルリポジトリにコピーされません。
オプション:ソース HTML から D0_AVATAR とロゴを上書きする
ソースとなるポスター HTML には通常、2 種類の base64 画像がインラインで埋め込まれています。
| アセット | 種類 | 特徴 | 用途 |
|---|---|---|---|
| D0 ロゴ | PNG、約 12–16 KB | .brand img または最短の PNG | すべてのシーンの左上 |
| D0_AVATAR | JPEG、約 100 KB | .av または最長の JPEG | TG コンポーネントのアバター |
python3 - << 'EOF'
import re, base64, os
with open('SOURCE.html', 'r') as f:
src = f.read()
os.makedirs('assets', exist_ok=True)
# ロゴ (最短の PNG)
pngs = re.findall(r'(data:image/png;base64,[A-Za-z0-9+/=]+)', src)
logo_b64 = min(pngs, key=len)
with open('assets/d0-logo.png', 'wb') as f:
f.write(base64.b64decode(logo_b64.split(',')[1]))
# D0_AVATAR (最長の JPEG)
jpegs = re.findall(r'data:image/jpeg;base64,([A-Za-z0-9+/=]+)', src)
avatar = max(jpegs, key=len)
with open('assets/d0-avatar.jpg', 'wb') as f:
f.write(base64.b64decode(avatar))
print("assets/ ready:", os.listdir('assets'))
EOF
フォント(手動の場合)
mkdir -p fonts
cp /path/to/d0-hyperframe/assets/fonts/*.woff2 fonts/
# 必要:InstrumentSerif-Regular.woff2, InstrumentSerif-Italic.woff2
# OpenSans-400/600/700/800-normal.woff2
プロジェクトの初期化(手動の場合)
npx hyperframes@latest init . --non-interactive --example blank --skill=d0-hyperframe
Step 1 — ブランドトークン(CSS)
:root {
--bg: #06060d;
--pu2: #b3a8f7;
--white: #f2f2f5;
--t2: #8d8d98;
--t3: #5a5a66;
--green: #3ddc84;
--red: #f87171;
--gold: #f7a93a;
--bdr: #23232c;
--bdr2: #2e2e39;
--grad: linear-gradient(120deg, #5468F5 0%, #9B4FD6 34%, #C25C9A 62%, #DD8B57 100%);
}
@font-face { font-family:'Instrument Serif'; src:url('fonts/InstrumentSerif-Regular.woff2') format('woff2'); font-weight:400; font-style:normal; }
@font-face { font-family:'Instrument Serif'; src:url('fonts/InstrumentSerif-Italic.woff2') format('woff2'); font-weight:400; font-style:italic; }
@font-face { font-family:'Open Sans'; src:url('fonts/OpenSans-400-normal.woff2') format('woff2'); font-weight:400; }
@font-face { font-family:'Open Sans'; src:url('fonts/OpenSans-600-normal.woff2') format('woff2'); font-weight:600; }
@font-face { font-family:'Open Sans'; src:url('fonts/OpenSans-700-normal.woff2') format('woff2'); font-weight:700; }
@font-face { font-family:'Open Sans'; src:url('fonts/OpenSans-800-normal.woff2') format('woff2'); font-weight:800; }
Step 2 — ステージ構造(永続ステージ + コンポーネントレイヤー)
コアモデル: 1 層の固定背景、コンテンツはコンポーネントに分解。 背景 + 固定要素 (ロゴ/フッター/上部レール) は永続レイヤーに属し、全体を通して動きません。タイトル、データ、TG パネル、チャートなどはそれぞれ独立した
.blkコンポーネントであり、各々が個別に入場・退場します。トランジションはコンポーネントレベルで行われ、画面全体ではなくなります——これが「PPT のカチッという切り替え」から脱却するための基本です。
HTML スケルトン
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>D0 · [動画タイトル]</title>
<style>/* トークン + シーン CSS */</style>
</head>
<body>
<div id="main"
data-composition-id="main"
data-start="0"
data-duration="[総秒数]"
data-width="1920"
data-height="1080">
<!-- ── 永続レイヤー:全体を通して動かない、tl アニメーションは記述しない(Step 3 参照) ── -->
<div class="bg"></div>
<div class="rail"></div>
<div class="logo"></div>
<div class="foot-tag">Agentic Trading for Every Market.</div>
<div class="foot-url">getdonut.ai</div>
<!-- ── コンテンツレイヤー:ビートごとにグループ化、コンポーネントはそれぞれ異なるタイミングで出入り(Step 6 参照) ── -->
<!-- beat 1 -->
<div class="blk" id="b1-title">...</div>
<div class="blk" id="b1-sub">...</div>
<!-- beat 2 -->
<div class="blk" id="b2-stat">...</div>
<div class="blk" id="b2-tg">...</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<script>/* GSAP タイムライン */</script>
</body>
</html>
ステージ CSS(永続レイヤー + コンテンツレイヤー)
/* ステージコンテナ */
#main {
position: absolute; inset: 0; overflow: hidden;
font-family: 'Open Sans', sans-serif; color: var(--white);
}
/* 永続背景 — 全体を通して固定、tl アニメーションには決して入れない */
.bg {
position: absolute; inset: 0; width: 1920px; height: 1080px; z-index: 0;
background: var(--bg);
background-image:
radial-gradient(ellipse 46% 58% at 76% 50%, rgba(150,128,255,.16) 0%, transparent 60%),
radial-gradient(ellipse 62% 50% at 8% 4%, rgba(110,100,255,.10) 0%, transparent 56%);
}
/* 上部グラデーションレール — 固定、動画全体で 1 本 */
.rail {
position: absolute; top: 0; left: 0; right: 0; height: 5px; z-index: 10;
background: var(--grad);
}
/* コンテンツコンポーネント — アニメーションの最小単位、デフォルトで非表示、それぞれ出入り */
.blk { position: absolute; opacity: 0; z-index: 2; }
従来モデルでは、各画面全体
.sceneが独自の背景とブロック全体のスライドを持っていました → PPT 感 + カクツキの原因。 新しいモデルは1 層の固定背景のみを持ち、コンテンツを.blkコンポーネントに分解し、トランジションはコンポーネントレベルで発生します(Step 6 参照)。
Step 3 — 固定要素仕様(非交渉)
以下の 3 つの要素はすべてのシーンで一貫しており、入場または退場アニメーションを記述しません。
| 要素 | 位置 | フォントサイズ | 説明 |
|---|---|---|---|
| Donut ロゴ | 左上 top:40px left:72px | 200×76px | CSS background、<img> は使用しない |
| Agentic Trading for Every Market. | 左下 bottom:48px left:72px | 27px | Instrument Serif italic、--pu2 |
| getdonut.ai | 右下 bottom:50px right:72px | 14px | Open Sans、--t3 |
/* ロゴ — CSS background、opacity:0 は記述しない */
.logo {
position: absolute; top: 40px; left: 72px; z-index: 10;
width: 200px; height: 76px;
background: url('assets/d0-logo.png') left center / contain no-repeat;
}
/* フッター — 直接表示、アニメーションなし */
.foot-tag {
position: absolute; bottom: 48px; left: 72px; z-index: 10;
font-family: 'Instrument Serif', serif; font-style: italic;
font-size: 27px; color: var(--pu2);
}
.foot-url {
position: absolute; bottom: 50px; right: 72px; z-index: 10;
font-size: 14px; font-weight: 700; letter-spacing: .06em; color: var(--t3); opacity: 0;
}
固定要素は永続レイヤーに属し、0 フレーム目から表示され、tl アニメーションは一切記述しません。
foot-tagは直接表示されます。foot-urlは一度だけ軽いフェードインを残してもかまいませんが、必須ではありません。
Step 4 — レイアウト原則
左右のバランス、中央の空白をなくす
- 2 カラムレイアウトを優先:テキスト/データは左、TG コンポーネント/画像は右
- 右側にキャラクター背景画像がある場合:絶対配置で右側全体に敷き詰め、左から右へのグラデーションマスクを重ねる
.scene-char {
position: absolute; top:0; right:0; bottom:0; width:62%;
background: url('assets/bg-char.png') right center / cover no-repeat;
z-index: 0;
}
.scene-char::after {
content:''; position:absolute; inset:0;
background: linear-gradient(to right,
var(--bg) 0%, rgba(6,6,13,.82) 18%, rgba(6,6,13,.45) 45%, transparent 100%);
}
- 左側のテキストエリアには
position: relative; z-index: 2を設定し、背景画像の上に重なるようにする
最小フォントサイズ基準
D0 Thorp Engine 動画を基準として、以下を下回ってはいけません。
| 階層 | 最小フォントサイズ | フォント |
|---|---|---|
| メインタイトル | 80px | Instrument Serif |
| シーンサブタイトル | 56px | Instrument Serif |
| Kick label / eyebrow | 18px | Open Sans 700 uppercase |
| 本文 / feature bullet | 22px | Open Sans |
| データアンカー (stat-val) | 48px | Instrument Serif |
| フッタータグ | 27px | Instrument Serif italic |
| TG コンポーネント内部テキスト | 13px(許容される最小値) | Open Sans |
Step 5 — TG コンポーネント仕様
アバター:必ず D0_AVATAR を使用すること
/* 大きいアバター(ヘッダー、40px) */
.av { width:40px; height:40px; border-radius:50%; flex:none;
background: url('assets/d0-avatar.jpg') center/cover no-repeat; }
/* 小さいアバター(メッセージ行、28px) */
.mav { width:28px; height:28px; border-radius:50%; flex:none; margin-top:2px;
background: url('assets/d0-avatar.jpg') center/cover no-repeat; }
<img> タグは使用しないでください。HyperFrames が重複したメディアノードを検出するのを防ぐためです(lint warning)。
グラデーションによるフォールバックは使用しないでください。実際の D0_AVATAR ファイルでなければなりません。
TG パネル構造
<div id="tg-panel">
<div class="tg-chrome">
<div class="dot r"></div><div class="dot y"></div><div class="dot g"></div>
<div class="tg-url">t.me / D0_Bot</div>
</div>
<div class="tg-hdr">
<div class="av"></div>
<div>
<div class="tg-nm">D0</div>
<div class="tg-st">bot · online</div>
</div>
</div>
<div class="tg-body">
<div class="msg-user">ユーザープロンプト</div>
<div class="msg-bot">
<div class="mav"></div>
<div class="bbubble">D0 の返信内容</div>
</div>
</div>
</div>
Step 6 — トランジションルール(非交渉)
コア原則: 背景は動かさず、コンポーネントを動かす。 「PPT 感」は、背景を含む scene 全体が一緒にスライドアウトすることから生まれます——視聴者は安定したアンカーを失います。 「カクツキ」は、out アニメーションと in アニメーションが首尾相接して発生します——その中間に一瞬の完全な静止が生まれます。 両方の解決策は同じです:固定背景 + コンポーネントのクロスステガー(入退場のオーバーラップ)。
デフォルトトランジション: コンポーネントレベルのクロスステガー(element cross-stagger)
背景 + 固定要素(ロゴ/フッター/レール)は全体を通して動きません。ビートが切り替わるたびに、コンテンツコンポーネントだけを動かします。
- 古いビートのコンポーネントが順次退場(stagger、
.inease、微妙な移動 + フェードアウト) - 新しいビートのコンポーネントが順次入場(stagger、
.outease) - 両者の時間は約 0.2–0.3 秒オーバーラップします——古いものがまだ退場し終わらないうちに、新しいものが入場を開始します。これがカクツキをなくすための鍵です。
// ビート切り替え: 古いグループがクロスフェードアウト、新しいグループがクロスフェードイン、時間はオーバーラップ(首尾相接ではない)
function beat(outEls, inEls, t) {
outEls.forEach((el, i) =>
tl.to(el, { opacity:0, y:-30, duration:0.45, ease:'power2.in' }, t + i*0.06));
inEls.forEach((el, i) =>
tl.fromTo(el, { opacity:0, y:40 },
{ opacity:1, y:0, duration:0.6, ease:'expo.out' }, t + 0.2 + i*0.08)); // +0.2 = オーバーラップ
}
// 使用法:beat(['#b1-title','#b1-sub'], ['#b2-stat','#b2-tg'], 4.0);
- 移動量は小さい(20–40px)。方向は統一してもよい(例:すべて上方向に移動して「ページをめくるような空気感」を演出)が、背景は決して参加しない。
- コンポーネントが多い場合は、stagger によって画面に常に動きがあるようにし、「同時入場 → 一緒に静止」を避ける。
いつ画面全体のトランジションを使うか
大きなセクション区切り(例:ブランドタイトルカード → データセクション)でのみ例外的に使用し、その場合もコンテンツレイヤーのみを動かし、背景は動かさない——該当セクションのコンポーネントを #grp コンテナでラップしてから動かします。
// Push-slide(コンテンツコンテナのみを動かし、背景 .bg は動かさない)
tl.to('#grpA', { x:-1920, duration:0.55, ease:'power3.inOut' }, t);
tl.fromTo('#grpB', { x:1920, opacity:1 }, { x:0, duration:0.55, ease:'power3.inOut' }, t);
// Blur crossfade(感情的なトランジション、通常は最後の遷移に使用)
tl.to('#grpC', { filter:'blur(20px)', opacity:0, duration:0.65, ease:'power2.in' }, t);
tl.fromTo('#grpD', { filter:'blur(20px)', opacity:0 },
{ filter:'blur(0px)', opacity:1, duration:0.65, ease:'power2.out' }, t);
❌ 禁止: 背景を含む scene 全体のスライドアウト、コーナーへのズーム、画面全体のズーム、out/in の首尾相接によるオーバーラップなし。
Step 7 — GSAP アニメーションルール
必ず遵守すること
const tl = gsap.timeline({ paused: true });
// ...tweens...
window.__timelines['main'] = tl;
tl.from()ではなくtl.fromTo()を使用する(HyperFrames の seek 決定性を確保するため)- 繰り返しアニメーションには
Math.floor(duration / cycle) - 1を使用し、repeat: -1は禁止 - 同じ要素に複数のアニメーショングループがある場合は
overwrite: 'auto'を追加する - 入場には
.outeasing、退場には.ineasing を使用する
レイアウト優先、アニメーションはその後
まず要素の最終状態(CSS による配置)を記述し、その後で fromTo を使って入場パスを記述します。
// ✅ 正しい
tl.fromTo('#title', { opacity:0, y:40 }, { opacity:1, y:0, duration:0.8, ease:'expo.out' }, 0.3);
永続レイヤーにはアニメーションを追加しない
.bg、.rail、.logo、.foot-tag、.foot-url は全体を通して固定され、アニメーションは .blk コンポーネント上でのみ発生します。背景が一度動き出すと、即座に PPT 感に戻ります——トランジションはコンテンツレイヤーのみを動かします(Step 6 の beat() 参照)。
Step 8 — Lint & プレビュー
npx hyperframes lint # エラーを 0 にする必要があります
npx hyperframes preview # http://localhost:300x、ホットリロード
| 一般的な warning | 対処法 |
|---|---|
overlapping_gsap_tweens | overwrite:'auto' を追加 |
duplicate_media_discovery_risk | <img> の代わりに CSS background:url() を使用 |
composition_file_too_large | 単一ファイルが 500 行未満の場合は無視可能 |
依存関係とアセットの出典
d0-brandスキル: フォント、フォントサイズ、ブランド原則の最終的な権威であり、本スキルと一緒にインストールする必要があります。- HyperFrames CLI:
npx hyperframes@latestで必要に応じて取得します。 - GSAP: 例では
3.14.2CDN バージョンを固定して使用しています。バージョンをアップグレードする場合は、最初に lint と seek 検証を再実行してください。 assets/fonts/: Instrument Serif と Open Sans。ライセンスファイルは対応するフォントディレクトリにあります。assets/brand/: D0 ロゴと D0 アバター。許可を得た Donut チームメンバーのみが D0 ブランドコンテンツに使用できます。