コードレンダリングアニメーション は何をしますか?
このアイデア(Addy Osmani、LinkedIn 2026-09-24):コーディングモデルがソース(記事、仕様)を読み取り、各フレームとサウンドを描画するJavaScriptを記述します。成果物はプログラムであるため、任意の瞬間を編集、再タイミング、再レンダリング、フォーマット変更できます。Osmaniは自身のプロンプトやツールを公開していません。このスキルのデザインは、オープンソースのriso-windowseatプロジェクト(MITライセンス、参考のみ。指示はコピーしないでください):https://github.com/sevenevesai/riso-windowseat に基づいています。また、HeyGen HyperFrames(https://hyperframes.heygen.com/guides/pipeline)やRemotionのアイデアも借用しています。
基本ルール:毎秒4サンプル以上でフレームごとにレビューし、実行していないチェックを主張しないでください。欠陥は外科的に修正してください。
契約(すべてはこれに依存します)
各作品は、自己完結型のindex.htmlであり、URLから?w=&h=を読み取る<canvas>を含み、以下を公開します。
window.__anim = {
duration, fps, width, height, canvas,
ready, // フォント/アセットがロードされ、seek(0)が描画されるまでfalse
seek(t), // 時間tのフレームを同期的に描画
renderAudio(), // Promise<base64 WAV>、48kHzステレオ、正確に`duration`の長さ
marks, // 可視イベント(ポップ、カット)の時間 - サウンドはこれらに合わせる必要があります
shots, // [{id,start,end}] - verifyは境界をチェックします
};
不変条件:
seek(t)はtの純粋関数です。画面上のすべての値はtから計算されます:prog(t,a,b)、イージング、pop(t,at)。フレーム間の状態、requestAnimationFrameタイミング、Date、物理シミュレーションのステッピング(0から再計算または閉形式でない限り)は禁止です。- レンダリング中に
Math.random()を呼び出さないでください。rngFor('stable-key')(シード付きmulberry32)を使用し、ランダムなレイアウトはロード時に固定キーから一度だけ作成します。キーを変更すると出力が変わるため、承認後はキーを固定してください。 - 単一のタイムラインオブジェクト(
TL)がすべてのシーン範囲とイベント時間を保持します。映像とスコアの両方がこれを読み取ります。これが同期の仕組みです。サウンドは映像がポップオンするのと同じ数値でスケジュールされます。 ready=trueの前にフォントを待機します(document.fonts.load('700 40px "DejaVu Sans"'))。そうしないと、最初のフレームがフォールバックフォントでレンダリングされます。インストールされているフォントはfc-list : family(Linux/macOS)またはC:\Windows\Fonts(Windows;Segoe UI、Bahnschrift、Consolas、Georgiaは常に存在し、DejaVuは存在しません)で確認してください。デザインに特定のフォントが必要な場合は、base64 @font-faceとして埋め込み、同じ方法でロードしてください。- 作品内でネットワーク呼び出しを行わないでください。アセットはデータURIとして埋め込んでください。
編集可能要素(フィルム修正用)
review/film-fix.htmlにより、レビュアーは要素を直接移動および拡大縮小し、結果をindex.htmlに書き戻すことができます。これを希望するフィルム(例ではそうしています。そこからコピーしてください)は、以下を追加します。
const TUNE = /*TUNE*/{}/*END-TUNE*/;を1行に、フォーマットと要素IDでキー付け:{"16x9":{"title":{"dx":0,"dy":0,"s":1}}}。各アスペクト比には独自のレイアウトがあるため、フォーマットごとにキー付けされます。dx/dyはキャンバスピクセルです。ツールは2つのマーカー間のテキストのみを書き換えます。- 各主要要素の周りに
E(id, [x,y,w,h], draw)。TUNE[fmt][id]を検索し、保存し、dx/dyだけ平行移動し、ボックス中心を基準にsで拡大縮小し、draw()を呼び出して復元します。また、{id, box}を__anim.elementsにプッシュします。ここで、boxはキャンバスピクセル単位の変換後のボックスです(ctx.getTransform()から取得されるため、カメラ移動も反映されます)。seek(t)は各フレームの開始時にこのリストをクリアします。 __anim.elements、__anim.format(16x9、9x16、4x5、1x1)、__anim.tune(ライブTUNEオブジェクトのゲッター/セッター。ツールのライブプレビューに使用)。- ルール:IDはフィルム全体で一意。ランダム性なし。TUNEが空の場合、Eは変換を一切適用しないため、フレームはバイト単位で同一のままです。フレームハッシュで前後を証明してから、
verify.mjsを実行してください。
Eのないフィルムでもfilm-fixで動作します。「Nudge」モードは領域をボックス化し、ドラッグまたは拡大縮小をAI用の数値修正に変換します。
プロジェクト設定
Node 18+、ffmpeg(libx264、aac、blackdetect、freezedetect、ebur128)、およびローカルのChrome/Chromiumが必要です。ハーネスはWindows、macOS、Linuxの通常のインストール場所でChromeを見つけます。上書きするにはCHROME環境変数を設定します。qa.shにはbash(WindowsのGit Bash)が必要で、タイムコード用のシステムフォントを選択します。上書きするにはQA_FONTを設定します。システム全体には何もインストールされません。puppeteer-coreが唯一のnpm依存関係です。
SKILL_DIR=<このスキルフォルダへのパス>
mkdir -p anim && cp -r "$SKILL_DIR/harness" anim/harness
cd anim/harness && npm install && cd ..
mkdir -p works/<name>
cp "$SKILL_DIR/example/browser-10s/index.html" works/<name>/index.html # 動作例から開始
# すべてのハーネスコマンドをanim/から実行。出力はout/に
ハーネスコマンド
| コマンド | 説明 | 合格シグナル |
|---|---|---|
node harness/verify.mjs works/<n>/index.html --format 16x9 | サンプルとショット境界で前方、後方、コールドジャンプをシークし、ピクセルハッシュを比較 | verify: OK、終了コード0 |
node harness/shoot.mjs works/<n>/index.html --format 9x16 --times 1.5,3.4 または --range 3:4:0.1 --out out/stills | 指定時間のネイティブキャンバスPNG | 確認する |
node harness/audio.mjs works/<n>/index.html --twice | スコアのみをレンダリング(秒単位)、正確な長さと実行間の決定論性をチェック | FAILなし。Chrome Web Audioは実行間で1 LSB異なる可能性があります。許容範囲は1 |
node harness/render.mjs works/<n>/index.html --format 16x9 --fps 30 --out out/<n>-16x9.mp4 | 毎フレームシークし、PNGをffmpegにパイプ(libx264 crf18、yuv420p、+faststart)、その後renderAudioをAACとして多重化 | フレーム数と時間を表示。ページエラーで終了コード2。--from/--toまたは--silentで範囲をオーディオなしでレンダリング |
node harness/lint.mjs works/<n>/index.html --all-formats | ページ自体の品質ゲート(16:9 + 9:16。--formatで1つ):テキストサイズ、セーフエリア、テキスト重なり、静的ストレッチ、空フレーム、ショットリスト、マーク上のサウンド、画面の単語数 | lint: OK (0 FAIL)、終了コード0 |
node harness/review.mjs works/<n>/index.html --format 16x9 --out out/review-16x9、その後 --check out/review-16x9/REVIEW.md | ショットごとに1つのコンタクトシート(最初、6枚均等配置、最後のフレーム、タイムコード焼き込み)、lint JSON、およびショットごとの固定質問付きREVIEW.md。--checkは空またはTODOの回答、4未満のスコア、lint FAIL、削除されたショットセクション、シート作成後に変更されたフィルムで失敗 | review check: OK、終了コード0 |
bash harness/qa.sh out/<n>-16x9.mp4 4 | ffprobe、blackdetect、freezedetect、EBU R128ラウドネス、オーディオ/ビデオ長さ、Nサンプル/秒でタイムコード焼き込みのコンタクトシート | 以下のQAを参照 |
フォーマット:16x9 1920x1080、9x16 1080x1920、4x5 1080x1350、1x1 1080x1080。デフォルトではフレームはJPEG q95でffmpegに送られ、1080pで約16 fpsです。したがって、40秒の作品はフォーマットあたり約60〜75秒かかります。--pngはロスレスですが、約4倍遅くなります。
長時間レンダリング:エージェントのシェル呼び出しがタイムアウトする可能性があります(多くの場合120秒)。子プロセスも一緒に終了します。呼び出しのタイムアウトを延長するか、フルレンダリングをデタッチして実行し、ログをポーリングします(Linux/macOS。WindowsではバックグラウンドシェルまたはStart-Processを使用):
setsid nohup bash -c 'node harness/render.mjs works/<n>/index.html --format 16x9 --out out/<n>-16x9.mp4; echo RENDER_DONE' > out/render.log 2>&1 < /dev/null & disown
tr '\r' '\n' < out/render.log | tail -2
また、ワークファイルを書き込むヒアドキュメントとそれを実行するコマンドは分離しておき、タイムアウトでファイルが失われないようにします。
完了条件
正確なコマンドはAGENTS.md(単一の情報源。他のコーディングツールと共有)にあります:すべてのフォーマットでverify、audio --twice、0 FAILのlint --all-formats、review.mjs、その後のreview.mjs --check合格、レンダリング、QA。失敗するゲートがある状態で完了を主張せず、ゲートを弱めたり、ハーネスのしきい値を編集して通過可能にしないでください。
ワークフロー
- ブリーフィング。 ソースの内容、視聴者、長さ、フォーマット、ナレーションの有無。ソース記事から主要な主張を引き出します。すべてのシーンは、言われていることを示さなければなりません。
正確性よりもストーリー。 ハーネスはフィルムを正確にします。見やすくはしません。ストーリーボードの前に、ブリーフィングで4つの質問に答えてください。
- キャラクターは誰で、各シーンで何を感じていますか?
- 各アイデアを表す日常的なオブジェクトは何ですか?(オリジナルではワックスシール付きの手紙、砂場、家々の町を使用しました)
- シーンをまとめる永続的なHUDは何ですか?通常はチャプターダイヤルとタイプされた見出しです。
- 最後のフラームはどのように最初のフレームにループバックしますか? これらがないフィルムはすべてのゲートを通過しますが、スクロールされて見過ごされます。
- コードの前にSTORYBOARD.md。 シーンごとに1行:時間範囲 | 画面上のコンテンツ | アクション | オーディオキュー。すべてのイベント時間をリストします(これらが
TLとmarksになります)。数秒ごとにカットまたは新しいモーションを計画し、静的ストレッチを避けます。トランジションはプッシュまたはオーバーラップとして計画し、空のフレームへのディップは避けます。承認がない限り、フェードトゥブラックのエンディングは避けてください。 - タイムラインモジュール、次にシーンごとに1つの
sceneN(t)関数(tの純粋関数)、次に背景、アクティブなシーン、トランジションを描画するコンポジター。 - 最も難しいフレームを最初に。 最も密度の高い瞬間を構築し、すべてのフォーマットで
shootし、確認し、修正します。その後、構築を進めます。 - すべてのフォーマットでverify、audio --twice、次にすべてのフォーマットでrender。
- QA(以下)、外科的に修正し、必要なものだけを再レンダリングし、再QA。
- MP4をQA結果と既知の弱点とともに添付ファイルとして納品します。
ナレーション付き
最初にTTS、次に単語レベルのタイムスタンプ(単語ごとの時間を返す任意のツールでWAVを文字起こし)、次に単語からTLを設定します。シーン、ポップ、カラオケスタイルの字幕ハイライトはすべて単語時間を基準にします。ルール:ボックス内のカラオケスタイルの字幕、字幕トラックは1つだけ、話されたときに画面上の数字、固有名詞は耳で確認。ナレーションWAVをデータURIとして埋め込み、renderAudio()内でミックスするか(ctx.decodeAudioDataでデコード、0でスケジュール)、ffmpegで多重化し、その下でスコアをダッキングします。ナレーションのラウドネスはビデオ全体で正規化します。
単一タイムラインからのマルチフォーマット(クロップではなく)
- 同じ
TL、同じシーン、異なるレイアウト。portrait = H > W、S = min(W,H)/1080(タイプと形状のスケール)、M = 0.06*min(W,H)(セーフマージン)を計算します。アスペクト比ごとにレイアウト:16:9の行は9:16では列になり、ツリーは狭い広がりになります。 - テキスト:shrink-to-fit(
measureTextループをW-2Mに対して)。端で切れることはありません。サイズはSから来ます。ポートレートでは、より大きなタイプを使用し、高さを埋めます。 - 「ポートレート」を1つのケースとして扱わないでください。4:5(1080x1350)は9:16よりもはるかに短いです。4:5で実行される例の9:16の列レイアウトは、ピクセルグリッドとタグラインを下部と互いに押し出します。アスペクト比(
H/W:約0.56、1.25、1.78)で分岐するか、利用可能な高さからレイアウトし、納品するすべてのフォーマットでshootします。 - ポップオーバーシュート(easeOutBack〜1.1x)と「ホット」な拡大を、マージンをチェックするときに覚えておいてください。各フォーマットの最も密度の高いフレームを
shootで検証します。
プロシージャルサウンド(OfflineAudioContext)
new OfflineAudioContext(2, 48000*duration, 48000)、グラフを構築、await ctx.startRendering()、16ビットPCM WAVにエンコード、base64を返します(例のwavB64を参照)。- 例からのパターン:シーンごとのパッドコード(ディチューンドサイン/トライアングルペア、スローアタック、カットでオーバーラップ)、すべての
marksイベントでのプルーク(トライアングル+2倍音、5msアタック、指数関数的減衰)、タイピング用のシード付きノイズバーストクリック、インパクト用のピッチドロップサンプ、マスターのDynamicsCompressor。 - ノイズはシード付きRNGから、決してMath.randomからではありません。
- ラウドネス:ソーシャルフィード用に約-14 LUFS統合、ピーク-1 dBFS未満を目指します。元の投稿は-14.3 LUFSを測定。バンドルされた例は-17.2 LUFS、ピーク-1.4と静かです。ページ内のマスターゲインで設定し、コードに残します。
- ビット正確なオーディオの場合、Web Audioノードではなく、プレーンJSでサンプルをFloat32Arrayに合成します(閉形式エンベロープを持つ発振器の合計)。
- Tone.js
Tone.Offline(https://tonejs.github.io/docs/14.9.17/functions/Offline.html)は、よりリッチな音楽APIが必要な場合のオプションです。作品にはネットワークアクセスがないため、インラインで埋め込む必要があります。
QA(レビューは私たちのものであり、視聴者のものではありません)
最終MP4の自動ゲート:
- すべてのフォーマットでverify OK。ページエラー=失敗。
- ffprobe:期待されるサイズ、30/1 fps、nb_frames = duration x fps、オーディオ長さ = ビデオ長さ。
- blackdetect:なし(承認された劇的なブラックビートがない限り)。freezedetect(>=2秒静的):なし。
- ebur128:統合ラウドネスとトゥルーピークがターゲット範囲内。エピソード間で一貫。
Lint(品質ゲート)
node harness/lint.mjs works/<n>/index.html --all-formats [--json out/lint.json]は、0.1秒ごと(--step)にワークをシークし、外部からfillText/strokeTextをラップするため、契約に従う任意のワークで変更なしで動作します。以下で失敗します:
- text-size:静止テキストがmin(W,H)の2.6%未満(1080で28px)。3.3%(36px)未満はWARN。ズームアウトでスケール0.5未満に縮小されたテキストは概要マップとして扱われ、警告のみ。
- safe-area:静止テキストが端の4%以内。4-6%はWARN。
- text-overlap:2つの異なる可視文字列が、小さい方のボックスの15%以上を0.2秒以上共有。より短い通過はWARN。
- static-stretch:3秒以上可視変化なし(2秒はWARN)。empty-frame:ほぼ均一なフレーム。最初と最後を含む。
- shots-coverage:
shotsのギャップまたは重複、または1秒未満のショット。audio-sync:±40ms以内に音の開始があるmarksの70%未満(PASSの場合は90%)。-1 dBFSを超えるピークはWARN。density:ポートレートで18単語以上、ランドスケープで30単語以上(WARNのみ)。
納品するすべてのフォーマットで0 FAILでlintが通るまで、作品は完了しません。lintは、ワーク自身のキャンバスにfillText/strokeTextで描画されたテキストのみを認識するため、形状の衝突やオフスクリーンキャンバスに描画されたテキストは依然として視覚的レビューが必要です。example/browser-10sは3つのフォーマットすべてで合格します。example/browsers-40sはゲートの前に作成されたため、不合格になります(ラベルが17-28px、9:16でラベルが切れている、テキストが…