Opus Visual Motion Engine
視覚的な成果物のための規律あるパイプライン:計画 → テンプレートから構築 → レンダリング → QA → 修正 → エクスポート。品質は、レンダリング前後のゲート(チェックポイント)によって決まり、センスや好みに依存しません。手順は順番に従ってください。タスクが「単純に見える」という理由で、ステップを飛ばさないでください。
以下のパスは、このスキルのディレクトリ(与えられた skill_resources ベース)からの相対パスです。これを $SKILL と呼びます。
0. 最初に読む(無条件)
コードを書く前に、成果物に一致するリファレンスを読んでください。それらはそのトピックについての権威ある情報源です。
| 成果物 | 読むもの |
|---|---|
| 動画、モーショングラフィック、説明動画、製品動画 | references/video-generation.md、references/motion-design.md、references/javascript-animation.md |
| キネティックタイポグラフィ / タイプ主体の作品 | 上記の動画セット + references/typography.md |
| ランディングページ、ダッシュボード、モックアップ、UI | references/visual-design.md、references/typography.md |
| 図、模式図、データビジュアライゼーション | references/visual-design.md §4 |
| フレーミング / カメラの決定 | references/cinematography.md |
| 時間とイージング | references/animation-primitives.md |
| 同期する音楽または音声 | SKILL.md §10 + references/video-generation.md §6 |
実例は examples/*.md にあります。ブリーフに最も近いものを読んでください。
1. コードの前に計画を立てる(書き出す)
HTMLを作成する前に、あなたの返信(または出力の隣の PLAN.md)に計画を書いてください。頭の中だけにある計画はカウントされません。
UI / ページ
- 主要メッセージ:1文。視聴者は3秒以内に何を理解しなければなりませんか?
- 名前付きディレクション:名前、4~6個の名前付き16進カラートークンとその割り当てられた意味、役割を持つ書体、フォームの処理。対象の業界、素材、言葉遣いから導き出してください。おもちゃ屋とフィンテックのダッシュボードが同じディレクションを共有することはありません。
- 階層:すべてのブロックを PRIMARY / SECONDARY / TERTIARY でタグ付けします。PRIMARYだけが1つの大胆なジェスチャーを得ます。
- セクション進行:セクションごとに1行で、その役割と次のセクションへの引き継ぎ方を示します。
- レンダリング前の計算:テキスト幅 ≈
chars × 0.55 × font-size(サンセリフ)または× 0.5(セリフ);ボックス幅 =max(title_chars × 8, subtitle_chars × 7) + 24(14px時);行のフィット = Σ幅 + ギャップ ≤ コンテナ。すべてのコネクタをすべてのボックスに対してトレースします。もし交差する場合は、L字型のパスをルーティングします。
動画 / モーショングラフィック
- コアメッセージ:1文。もし2文必要な場合は、2つの動画があります。
- ビート:3~7。各ビートは「視聴者は今何を知っているのか?」に答えます。
- ビートごとのシーンカード:開始/終了時間(秒)、構成、カメラ、タイプサイズ、プリミティブ(2~4)、終了。
references/motion-design.md§2のテンプレートを使用します。 - シグネチャーモーメント:メカニズムが示される(ラベル付けされるのではなく)1つのフレームに名前を付けます(例:ノイズが信号にフィルタリングされる)。ストーリーがキャプションでのみ機能する場合は、ビートを再設計します。
- グローバルタイムライン:絶対タイムスタンプ。シーンのEXITは、次のシーンがそのスペースを要求する前に完了します。
DURATIONを真の終了時間に設定します(デフォルトは10~20秒;ナレーションがある場合のみ30~45秒)。
カードのないシーンは動画に属しません。
2. テンプレートから構築する(動画)
assets/composition-template.html を成果物の隣にコピーし、EDITブロックを埋めてください。ENGINEブロックは書き換えないでください。それらはすでに以下を提供します。
- 1920×1080の
#stage(1080×1920または1080×1080の場合はSTAGE_W/Hを変更)、任意のウィンドウにフィット。 window.DURATION、window.seek(t)、window.render(t)、window.SCENES:キャプチャ/エクスポートの契約(QAはSCENESからすべてのシーンをキャプチャします)。at()、ease.*、lerp、mulberry32(シード付きPRNG)、およびシーンの可視性とEXITのためのlayer()。- オーディオヘルパー
audioAt(t, "rms"|"low")、since(t, list)、hit(t, list, dur)はwindow.AUDIO(§10)を読み取り、それがない場合は0を返します。 - プレビュープレーヤー(スペース、←/→、Home/End)は、キャプチャ時とモーション軽減設定時に自動的にオフになります。
- 必要な3つのレイヤー(ライト、
prefers-color-schemeガード、[data-theme])のカラートークン。
ページの場合は、§6に従って通常の単一ファイルHTMLを記述します。
3. 決定論的時間(動画)
render(t)が唯一の状態ドライバです。すべての位置、不透明度、サイズ、数値、ダッシュオフセットは、秒単位のtから計算されます。seek(x)は、以前に何がレンダリングされていたかに関係なく、同一のフレームをレンダリングします。tは秒単位です。tを[0, DURATION]にクランプし、決して[0, 1]にはしないでください:秒を0..1にクランプすると、タイムライン全体が静かにフリーズします。- 禁止:
setTimeout、setInterval、タイムラインとしてのCSSアニメーション/トランジション、シードなしのMath.random()、状態におけるDate.now()。requestAnimationFrameは再生クロックを進め、render(clock)を呼び出すためだけに使用できます。テンプレートはすでにこれを行っています。 - ランダム性:ビルド時に
mulberry32(seed)から生成します。render内では決して行わないでください。 - SVGの描画には、
pathLength="1"、stroke-dasharray="1"、およびstroke-dashoffsetを1から0にするという3つすべてが必要です。ダッシュ配列がないと、線はフレーム0から完全に描画されたように見えます。 inline-blockの単語スパンは末尾のスペースを飲み込みます。スペースはmargin-rightに入れてください。
4. モーション文法
5つの動き。それぞれに1つの役割があります。もし動きがこれらのどれでもない場合は、削除してください。
- ENTER:ページまたはシーンごとに1つのオーケストレーションされたシーケンス。読む順序で、兄弟要素は60~120msの間隔。すべてのセクションでフェードアップはAIの特徴です。
- EMPHASIS:一度に1つの要素:スケールスウェル(≤1.2)、グロー、または意味を持つ色の変更。
- TRANSITION:状態の変更。変更された部分が動きます。それ以外はすべて静止。0.2~0.6秒。
- CAMERA:ステージレベルのスケール/移動。シーンごとに最大1回。0.8~1.2秒。テキスト読み上げ中は決して行わない。
- EXIT:0.3~0.5秒で完全に完了。シーン境界をまたいで中途半端な状態で要素が生き残ることはありません。ただし、意図的な連続性のアンカー(同一のまま持続するか、モーフィングする(状態を変える同じオブジェクト、これは利用可能な最強のトランジションです))は除きます。
トランジションは意味を持ちます:ハードカット=カテゴリ変更、モーフ=同じものの変化、パン=移動。決してデフォルトのクロスフェードをどこにでも使用しないでください。持続時間とイージングは references/animation-primitives.md にあります。一緒に動く隣接要素は、異なる持続時間またはオフセットを取得します。
5. スタックの決定
| 状況 | スタック |
|---|---|
| ページのマイクロモーション | transform/opacity に対するCSSトランジション、0.2~0.6秒;ループ ≤2秒、@media (prefers-reduced-motion: no-preference) 内 |
| 動画 / モーショングラフィック / キネティックタイプ | テンプレート + バニラ render(t)(デフォルト) |
| >6つのスタッガードトラックまたはSVGモーフオーケストレーション | GSAP UMD固定、1つの一時停止マスタータイムライン、seek(t) → tl.seek(t) |
| チャート | インラインSVG(デフォルト)または animation:false のChart.js UMD、16進カラー(キャンバスはCSS変数を読み取れません) |
| ジェネレーティブアート | randomSeed/noiseSeed を使用したp5.js |
| 3D(3Dがメッセージである場合のみ) | Three.js UMD固定;render(t) でスクリプト化されたカメラ |
結果に到達する最もシンプルなスタックを使用してください。外部スクリプトは、cdnjs.cloudflare.com または cdn.jsdelivr.net からの正確な固定バージョンでなければなりません。その他はすべてインライン化してください。すべてのフォントは、実際のフォールバックスタックを持ちます。
6. 技術的最低条件(交渉不可)
- 単一の
.htmlファイルでCSSとJSをインライン化、16 MB未満、画像/データはdata:URIとして。 - カラーは
:rootトークンとして;ダークレイヤーは@media (prefers-color-scheme: dark)内で:root:not([data-theme="light"])によってガードされ、:root[data-theme="dark"]の下でも繰り返されます;明示的なbody背景。 meta viewportにviewport-fit=cover;env(safe-area-inset-*)パディング;html/body にheight:100%、決して100vhではありません;広いブロックは自身のoverflow-x:auto内でスクロールします;グリッドはminmax(0,1fr)を使用します。- 表示される計算数値は、
Math.round、toFixed、またはIntl.NumberFormatを通過します。カウントアップは最終値に正確に到達します。 - アクセシビリティ:コントラスト ≥4.5:1(≥24pxのテキストの場合は ≥3:1)、可視フォーカス、
prefers-reduced-motionを尊重します(テンプレートは定常状態の最終フレームを示します)。 - 読みやすいタイプ:1080p動画フレーム内では20px未満のテキストはありません(H/54)。モバイルページでは、本文テキストは14px未満ではありません。
- ブラウザストレージはタイムライン状態を保持しません。ストレージが使用される場合、try/catchでラップします。
7. 一般的なAIデザインを避ける
ブリーフに自由度がある場合、以下のデフォルトには決して使わないでください(qa.mjs はこれらのいくつかを自動的にフラグします):
| 特徴 | 修正 |
|---|---|
クリーム色 #F4F1EA 系 + セリフ表示 + テラコッタ #D97757 系アクセント | 対象自身の素材から取ったパレット |
| ニアブラック + 1つの酸っぱいアクセント、主題に関係なく使用 | ディレクションが文章でそれを正当化する場合のみ許可 |
| 同一の角丸カード、どこでも1つの半径、同じソフトシャドウ | 階層ごとの半径トークン;カードは境界のあるオブジェクトのみ |
| 装飾としてのグラデーション、紫色のグラデーション、グラスモーフィズム | フラットフィル;最大1つのグラデーション、連続する物理的特性の場合のみ |
| トラッキングされたALL-CAPSのアイブロウ、ミドルドットのメタ文字列、リンクの「→」 | 文頭のみ大文字;コンテンツを繰り返すラベルを削除 |
| Inter / システムフォントをデフォルトに | この主題のために選ばれた書体、フォールバックスタック付き |
| 見出し内の1語だけアクセント | アクセントカラーが、作品内の他の場所で同じ意味に使用されるセマンティックトークンである場合のみ許可(例:コーラル = ノイズ) |
| 一般的なヒーロー:見出し + サブタイトル + CTA + スクリーンショット | 対象の世界で最も特徴的なもので始める |
| すべて中央揃え、すべてのセクションで同じグリッドリズム | セクションごとの配置システム;リズムを変える |
| すべてが一度にアニメーション、どこでも300ms | 持続時間マップ + スタガー(§4) |
大胆さは1箇所に使い、その後1つのアクセサリーを削除します。
8. レンダリング + QA(納品前に必須)
自動化されたQAを実行します。ファイルをヘッドレスでレンダリングし、証拠と判定結果を出力します。Node ≥18が必要です。スキルの新しいコピーでの初回実行は、$SKILL/scripts で npm install が必要です(scripts/node_modules が存在する場合はスキップします)。
node "$SKILL/scripts/qa.mjs" path/to/deliverable.html
WindowsのDSHでは、同じコマンドで pwsh ツールを使用して実行します。オプション:--step 0.25(高速シーン用の細かいグリッド)、--out <dir>、--mode page|video。
ビデオモード(DURATION + seek が存在する場合に自動)は、ステップごとにフレームをキャプチャし、以下をチェックします:禁止API、順不同の再訪問での決定論、フリーズしたタイムライン、無音、ほぼ空のスパン、未確定の最終フレーム、テキストの衝突、他のグラフィック上のテキスト、クリッピング、コントラスト、最小タイプサイズ、決して描画されないダッシュオフセットライン、モーション軽減時のループ、コンソール/ページエラー、テンプレートの特徴。
ページモードはデスクトップ1440、モバイル390、ダークスキームをレンダリングし、同じレイアウトルールに加えてモバイルの水平方向のオーバーフローをチェックします。
出力は qa-<name>/ に配置されます:report.md(判定結果と調査結果)、report.json(フレームごとのインクカバレッジと変化)、contact-sheet.png(すべてのフレームを1つの画像に)、moments.png(各シーンの開始、中間、終了、window.SCENES から取得)、key_000..100.png、および frames/。
- シグネチャーモーメントと同期ポイントを明示的に渡します:
--at 5.4,9.1。 - 意図的なレイヤリング(意図的に積み重ねられたキネティックタイプ、フッテージ上のタイトル)は、
data-qa-allow-overlap属性を持つ要素の中に入れます。これにより、そのサブツリーのみの衝突とテキストオーバーグラフィックスのチェックがスキップされます。偶発的な衝突を黙らせるために使用しないでください。 fontのWARNは、第1候補のファミリーがロードされず、テキストがフォールバックにあることを意味します。レイアウトを判断する前に修正してください。すべての幅が間違っているためです。
終了コード0 = FAILなし。FAILはブロッキングです。 すべてのWARNは、修正されるか、あなたの批評で1行で正当化されます。
9. 批評ループ
- あなたのモデルが画像を読める場合:
contact-sheet.pngを開き、次に間違っているように見えるkey_*.pngを開きます。意図ではなくピクセルを判断します。 - あなたのモデルがテキストのみの場合:
report.mdとreport.jsonのフレームごとのテーブルを使用します。coverage< 0.015 はフレームが空であることを意味します。シーン全体でのdiffが0は何も動いていないことを意味します。diffのスパイクはカットを示します。各シーンカードのタイムスタンプをこれらの数値と照合します。 - 各質問に「✓」ではなく具体的な観察結果で答えてください:
- 構成:各キーフレームで、最初に目が行く場所はどこですか?それがメッセージですか?
- 階層:PRIMARYは間違いなくわかりますか?フレームごとに重要で動くものが1つだけありますか?
- メカニズム:シグネチャーモーメントは、キャプションを削除した状態でアイデアを示していますか?
- 連続性:すべての引き継ぎはきれいですか?境界で中途半端な状態のものはなく、アンカーは同一またはモーフィングしていますか?
- タイミング:オフセットは存在しますか?デフォルトの持続時間はありませんか?8語あたりの読み取り時間は ≥1.2秒ですか?
- タイポグラフィ / コントラスト / 密度:計画通りのサイズ、フレームあたり5つ以下の競合要素、ダークモードで読みやすいですか?
- ナラティブ:視聴者は1回の視聴でコアメッセージを繰り返せますか?
- 修正 →
qa.mjsを再実行 → 再批評。QAに合格し、批評に未解決項目がなくなるまで繰り返します。 - 最後の質問、正直に答えてください:「これは意図的にデザインされたように見えますか、それとも単に生成されたように見えますか?」 「生成された」の場合:何が一般的かを特定し、計画(§1)を修正し、再構築します。最初のレンダリングが納品物になることはほとんどありません。2~3回の生成を想定してください。
10. オーディオ同期(音楽または音声がある場合)
耳でタイミングを推測しないでください。オーディオをデータに変換し、render(t) にそれを読み取らせます:
node "$SKILL/scripts/audio-data.mjs" music.mp3 --inject composition.html # window.AUDIO をHTMLに書き込みます
AUDIO.beats(推定テンポでの等間隔グリッド)、AUDIO.onsets(ヒット、音符、音節)、およびフレームごとのAUDIO.rms/AUDIO.low(0..1;lowは <150 Hz のキック/ベースバンド)。- テンプレートヘルパーでモーションを駆動します:ビートパルス(EMPHASIS)には
hit(t, AUDIO.beats, 0.2)、キック反応スケールにはaudioAt(t, "low")、ヒットごとの表示にはsince(t, AUDIO.onsets)。ビート上にシーンカットを置きます:AUDIO.beatsからシーン開始を選びます。 - 同期は構築上フレーム正確です:パルスは、そのビート以降の最初のフレームに着地します(≤1フレーム、30fpsで≤33ms)。オーディオがt=0より後に開始する場合は、
--offset <s>を渡します。 - レベルは誰かが聞く前にチェックされます。クリップされたサンプルは終了コード1で終了します。サンプルピークが−1 dBFSを超えると警告が出ます。AACエンコーディングがクリップするためです。ラウドネスが−18..−12 LUFSの範囲外であることが報告されます(プラットフォームは約−14にノーマライズします)。
--bpmは推定テンポを上書きします。