Morph Edit (モーショングラフィック B-Roll)
あなたはクリエイターの動画向けにモーショングラフィッククリップを作成します。すべてのクリップはカットされずに変形し続ける1つのシェイプです。サイズ、角丸、色が状態ごとに変化し、内容の切り替え時には短いブラーがかかります。カーソルが実際のクリックやドラッグで変化を駆動し、すべての変化は発話された単語のタイミングに合わせられます。
出力は、クリエイターが自身のエディターに取り込むためのクリップセットです。また、クリップをカットインしたプレビュー動画レンダリング、クリップをステップ実行するためのHTMLページ、比較用のHTMLページも提供します。
必要なものはすべてこのスキルフォルダに含まれています。
engine/: モーションエンジン (motion.js)、ページシェル (base.css)、build.py(クリップ → スタンドアロンHTML)、render.js(フレーム単位レンダリング、モーションブラー対応)、beats.js(静止画コンタクトシート)、フォント (Geist、OFL)。scripts/:setup.sh、inspect_video.py、words.py、composite.py、make_pages.py。reference/engine-api.md: クリップの書き方。最初のクリップを書く前に読んでください。examples/opus-aoe2/: 実際の54秒動画向けの完成済みクリップ6つ。品質基準および出発点として使用してください。
以下、$SKILL はこのスキルのフォルダを意味します。ユーザーのプロジェクト内に motion/ フォルダを作成して作業してください。
1. セットアップ (初回のみ)
bash $SKILL/scripts/setup.sh ./motion
Node、Python 3、ffmpeg の存在を確認し、Playwright + Chromium を motion/node_modules にインストールします。すべてのエンジンスクリプトは NODE_PATH=./motion/node_modules を指定して実行してください。
2. インタビュー
AskUserQuestion ツールを使用し、1ラウンドで収めてください。必要な情報:
- 動画のパス。 パスを取得し、
motion/work/source.*としてコピーまたは参照します。 - 文字起こし。 任意。存在しない場合(またはプレーンテキストのみの場合)、
scripts/transcribe.pyで音声を文字起こしします(ステップ3参照)。これにより、SRTの推定値よりも正確な単語タイムスタンプが得られます。 - 密度:
- ライト:毎分2〜4クリップ、最も強い瞬間のみ。
- ミディアム(デフォルト):毎分4〜7クリップ。
- ヘビー:ほとんどのラインをカバーし、短い顔のギャップを残す。
- ルック:
- デフォルトパレット:ウォームグレーのキャンバス、白黒のコンポーネント、1つのオレンジアクセント。
- またはユーザーのブランド:色、オプションでフォント。ロゴ、スクリーンショット、ブランドカラーを
motion/inputs/にドロップしてもらい、それを参照します。
- 避けるべきもの、含めるべきもの。 顔を残すライン、表示する実際の数値や名前、再現する製品スクリーンショット。
ユーザーが既に回答した質問はスキップします。
3. フッテージの検査
python3 $SKILL/scripts/inspect_video.py motion/work/source.mp4 motion/work
python3 $SKILL/scripts/words.py transcript.srt > motion/work/words.txt
# 文字起こしがない場合(または正確な単語タイミングが必要な場合):代わりに音声を文字起こし
motion/.venv/bin/python $SKILL/scripts/transcribe.py motion/work/source.mp4 motion/work
transcribe.py は words.txt と transcript.srt を書き出します。計画を立てる前に、聞き間違いや用語の修正ができるよう、文字起こしをユーザーに提示してください。
自分で motion/work/contact.png を確認してください。video.json は解像度、fps、レイアウトセクションを提供します:
- full: 話者がフレーム全体を占めます。
- pip: 話者がボックス内に表示され、残りは空です。ボックスの位置と、ボックスサイズが変化または移動する場合の警告が含まれます。
クリップは動画の解像度とfpsに一致する必要があります。SRTからの単語タイミングは推定値(±0.2秒)です。そのように扱ってください。
4. 計画し、コードを書く前に承認を得る
クリップごとに1行のテーブルを表示します:
| # | In–Out | カバーするライン | どの単語でシェイプが何をするか | 処理 | 理由 |
クリエイティブディレクション:各クリップがフレームのどの程度を占めるかを決定します。 これは最も重要な判断です。クリップごとに決定し、理由を述べてください。
- 全フレームカットアウェイ。 フレーム全体が話者であり、そのラインが何かを 見せる 場合:製品、プロセス、比較、数値、チャプターの切り替え。
- アイデアの長さ(3〜10秒)だけカットアウェイし、その後顔に戻ります。
- フックの最初の1秒、または個人的・感情的・意見のラインではカットアウェイしないでください。
- カットアウェイの間には最低約2秒の顔を残してください。
- 空きスペースのパネル。 既に編集でスペースが確保されている場合:PiPボックス、分割、プレーンな領域。
- 空き領域の中央に配置された透過クリップ(
bg:null)をレンダリングし、すべての瞬間で話者のボックスにかからないサイズにします。 - 話者が画面に表示され続けるため、1つの連続モーフとして長時間(15秒以上)実行できます。
inspect_video.pyがボックスの変化を報告した場合、それが意図的かどうかを尋ね、最大のボックスにサイズを合わせるか、変化点でパネルを分割します。
- 空き領域の中央に配置された透過クリップ(
- 何もしない。 そのラインが話者についてのものか、別のクリップに近すぎる場合。
コンテンツルール:
- 被写体の実際のオブジェクト(フォルダ、端末、ファイル、製品、結果)を表示します。それらを言葉から引き出します。各アイデアを、
reference/engine-api.mdの語彙(ピル+クリック、進捗、チェック、ステータスアイランド、カード+ドラッグ、スライダー、トグル、タブ、チャート+ツールチップ、検索/フィルター、ファイルドラッグ&ドロップ、端末タイピング、サイドバイサイド比較、チャプターカード)からのインタラクションにマッピングします。 - 数値、引用、価格、結果を決して発明しないでください。 相対的なバー、スケルトンテキスト行、または文字起こしから取ったラベルを使用し、何が例示であるかを明記します。実際の数値は、ユーザーから提供された場合のみ使用します。
- クリップあたり1つのアイデア。連続した部分(PiPセクション全体)は、複数のステートを持つ1つの長いクリップにできます。
- 音楽ではなく発話に合わせてペースを調整します:1拍あたり1回の変化、約0.4〜1.2秒間隔。
ユーザーが計画を承認または編集するのを待ちます。
5. ビルド
reference/engine-api.md とサンプルに従って、motion/clips/NN-name.html にクリップごとに1つのフラグメントを記述します。その後ビルド:
python3 $SKILL/engine/build.py motion/dist motion/clips/*.html
各ステート変更を対応する単語に配置します:クリップローカル時間 = 単語時間 − クリップのイン点。
6. キーワードでの静止画確認
NODE_PATH=./motion/node_modules node $SKILL/engine/beats.js motion/dist/NN-name.html motion/work/NN.png 0.4 1.2 2.1 …
各ステートが落ち着いた時間と、モーフィング途中の瞬間をいくつか選びます。自分ですべてのシートを確認してください。詰まり、クリップ、読みにくさ、単語との不一致、またはカーソルがフレームからはみ出る箇所を修正します。その後もう一度確認します。パネルクリップはシート上で黒背景でレンダリングされるため、その状態で判断できます。
7. レンダリング
NODE_PATH=./motion/node_modules node $SKILL/engine/render.js motion/dist/NN-name.html motion/out/NN-name_0m32s40.mp4 30000/1001
- 動画のfpsを使用します。
- パネルクリップ(
bg:null)は.movで書き出す必要があります。これによりアルファチャンネル付きのProRes 4444が得られます。 - 各ファイルの名前は、タイムライン上のイン点(
0m32s40= 0:32.40)に基づいて付けます。 - レンダリングは1フレームあたり4サブフレームを生成するため、リアルタイムより遅くなります。マルチコアマシンでは2つのクリップを並行して実行してください。
8. 納品
motion/plan.json を書き出します(スキーマは reference/engine-api.md)。UI効果音については、各クリップにキューから取得した "sfx": [[クリップローカル時間, 種類], ...] リストを付けます:カーソルクリックに click と toggle、ドラッグ開始/解放に pickup/drop、シェイプモーフに whoosh、イントロとバッジに pop、バーに rise/fall、タイピングに type、アイテム出現に tick、スクリーンショットに shutter、チェックに success。その後:
python3 $SKILL/scripts/composite.py motion/plan.json motion/out/preview.mp4
python3 $SKILL/scripts/sfx.py motion/plan.json motion/out/preview.mp4 # UI効果音(ユーザーが希望する場合)
python3 $SKILL/scripts/make_pages.py motion/plan.json motion/out/preview.mp4
python3 $SKILL/scripts/make_gallery.py motion/plan.json motion/out/preview.mp4
すべて motion/out/ 内でユーザーが受け取るもの:
- クリップ
TIMING.md: ファイル、イン、アウト、カバーするライン、処理、例示しているものpreview.mp4: クリップがカットインされた動画、ハードカット、元の音声(SFX使用時はSFX付き)sfx/: 各クリップの効果音を個別のWAVファイルとして。エディターでミキシングするためindex.html: すべてのクリップをグリッドで同時再生、フルプレビューと他のページへのリンク(最初に開く)viewer.html: クリップをステップ実行compare.html: オリジナルとプレビューを同期させて、サイドバイサイド、重ね、ワイプで比較
プレビューはレビュー用であることを明確に伝えてください。最終的なカットでは、クリエイターが自身のエディターでクリップを配置し、タイミング調整、トランジション追加、オーディオミキシングを行います。
購読カード(オプション)
クリエイターに、コールトゥアクションのラインにエンドカードの購読カードと、1〜2つの短いリマインダーを追加するかどうかを尋ねます。テンプレートは $SKILL/templates/subscribe/ 内のクリップフラグメントです(end-card.html、reminder.html)。1つを motion/clips/ にコピーし、その後:
__CHANNEL__、__HANDLE__、__AVATAR__(データURIとしてのクリエイターのロゴまたは写真、画像がない場合はサークル内の文字)を置き換えます。これらは必ず尋ねてください。名前やハンドルを推測しないでください。<script>の先頭にある定数(IN、クリック時間、T)をクリエイター自身の言葉に再調整します。エンドカードは、クリエイターが発話した単語に合わせて「共有」「購読」「ベル」「いいね」「コメント」をクリックします。リマインダーは「購読」と「ベル」をクリックします。- 他のクリップと同様にビルド、静止画確認、レンダリング、
plan.jsonへの追加を行います。カードは透過.movパネルとしてレンダリングされるため、フッテージの上に配置されます。
キャプション(オプション)
発話言語のSRTと英語のSRTを作成します(焼き込みではなく、クローズドキャプションとしてアップロードするため)。motion/work/cap/ で作業:
motion/.venv/bin/python $SKILL/scripts/captions/find_gaps.py motion/work/words.txt "<video>": Whisperは長い動画では発話の連続部分全体を落とすことがあります。ギャップを出力するので、retx.py "<video>" "<gaps list>" motion/work/gapwords.json [language]で再文字起こしします。python3 $SKILL/scripts/captions/make_cues.py motion/work/words.txt motion/work/gapwords.json motion/work/capがキューと4つのチャンクを書き出します。templates/caption_brief.mdを使用して、4つのサブエージェント(チャンクごとに1つ)でチャンクを並行翻訳します。各エージェントは、すべてのキューに対して{"i","orig","en"}を含むout1..4.jsonを書き出します。- フラグが付いたキューを修正し、
python3 $SKILL/scripts/captions/build_srt.py motion/work/cap motion/out/captionsでcaptions_original.srtとcaptions_english.srt(各行最大42文字のバランスの取れた2行)を書き出します。 - 公開前にVLCまたは限定公開アップロードでSRTをテストするようクリエイターに伝えます。
スタイルデフォルト
ユーザーのブランドがこれらを上書きします。
- 色: キャンバス
#E9E7E2、インク#0B0B0B、白いコンポーネント、アクセント1色#FF5A1F。暗いフッテージ上のパネルクリップは明るいシェイプ(#F4F2EE)を使用します。 - タイプとアイコン: UIテキストはGeist、コード・ファイル名・端末はGeist Mono。1つのアイコンセット、1つのストロークウェイト(
M.icon)。 - モーション: せいぜい小さなオーバーシュートのスプリング。リーディングエッジとトレーリングエッジは異なるスプリングに乗るため、インジケーターが伸びます。各ステートがフレームを埋めるようにカメラがズームします。
- 禁止: バウンシーなイージング、パーティクル、グロー、UIクロームのグラデーション、混合されたアイコンストローク、デッドタイム、テンプレートのように見えるもの、捏造されたデータ。
注意点
- カメラが拡大縮小する要素に
will-changeを決して設定しないでください。テキストがぼやけてレンダリングされます。 - モーフィング中のコンテナ内でのテキスト入れ替えには、独自の出入りタイミング(
M.visdin/lin/lout)が必要です。そうしないと、古いテキストと新しいテキストが重なります。 mix-blend-mode: differenceラベルは、ブレンドモード自体を持つレイヤーに配置する必要があります。フィルターをかけた親要素がそれらを分離します。- ハイライトの下をスライドするアイテムには
M.FASTスプリングが必要です。そうしないと、ハイライト行が一瞬空になります。 - モーフィング中を含め、すべてのカメラズームでカーソルをフレーム内に維持します。
- SRTからの単語推定値はキュー内でドリフトします。可能な場合は、重要な変更をキューの最初または最後の単語に配置します。
- クリップは最後のステートを保持して終了します。コンポジットは、クリップのスロットがより長い場合、最後のフレームを保持します。