2 Models Available
監督は、5秒のAI動画クリップから最も強いフレームを1枚選び出し、シーケンスの次のショットの参照画像として使います。Astorieのキャンバス上で、元のクリップ(Seedance 2、Kling 3など)をフレーム抽出ツールノードに送り、選んだタイムコードまでスクラブし、抽出した静止画をNano Banana 2の画像編集ノードに接続するか、次の動画ノードの開始フレームとして直接使います。出力は参照として固定されたフレームで、次のショットの開始フレーム、画像編集チェーンの入力、あるいは承認済みテイクの主要な静止画として使えます。下記からモデルを選んで、フレーム収集のワークフローを体験してください。

スクロール連動のcanvasアニメーション用にAI動画からフレームを抽出するには、5段階のパイプラインを実行します:ループ可能なクリップを生成する、FFmpegでクリップを連番のフレームシーケンスに書き出す、それらのフレームを選別してWebPに変換する、drawImage()で現在のフレームをHTMLの<canvas>に描画する、そしてGSAP ScrollTriggerを使ってスクロール位置からフレームのインデックスを制御する、という流れです。結果として得られるのは、Apple風の「スクラブ」効果——スクロールするとカクつく<video>要素の代わりに、canvas上でクリップが1フレームずつ再生される仕組みです。
競合するチュートリアル(Codrops、Builder.io、Tympanus)はどれも、すでに手元にある動画ファイルから始まり、FFmpegとGSAPの後半部分しか教えてくれません。Astorieは上流の工程を1つのノードベースのキャンバスにまとめます:開始・終了フレームの制御で完璧にループするAIクリップを生成し、フレームを抽出・選別し、必要ならキービジュアルとなるフレームを4K化する——これらすべてを、コードを1行も書く前に完了できます。GSAPに渡す成果物は、ぼやけた画面録画ではなく、きれいに重複を取り除いたWebPシーケンスです。
このパイプラインは左から右へ4つの工程として読めます:(1) Astorieのキャンバス上で動画モデルを使ってループを生成する、(2) フレームシーケンスを抽出する(キャンバス内でのフレーム抽出、または後述のFFmpegによる代替方法)、(3) キービジュアルとなるフレームを選別し、必要に応じてアップスケールする、(4) GSAP ScrollTriggerでシーケンスを<canvas>にレンダリングする。1〜3はキャンバス上で行うクリエイティブな作業であり、4が実際にリリースするコードです。以下の各セクションでは、そのままコピー&ペーストできるコマンドとともに各段階を解説します。

ループ時にクリップがジャンプカットしてしまうと、スクロールスクラブは破綻して見えます。そのため元の動画は綺麗にループする必要があります。Astorieのキャンバス上で動画ノードを配置し、開始フレームと終了フレームの両方を制御する機能を使います:同じ画像を開始フレームと終了フレームの両方に設定すると、モデルは出発点に戻ってくる動きの軌道を補間します。開始・終了フレームの条件付けに対応しているモデルにはKling、Luma、Runway、Seedanceがあります。VeoやSora、Wanのようなテキストから動画を生成するモデルは、シームレスなループが不要なクリップに向いています。
プロンプトと開始・終了フレームを複数の動画ノードに一度に接続して、一括展開を実行します:1枚の元画像を同時にKling 3、Seedance 2、Runway Gen-4へ送り、それぞれのテイクをバージョントレイに残して、最もきれいにループするものを選びます。これは競合が裸の動画ファイルからではできない部分です——あなたは抽出の前に最良の素材を選んでいるのであって、すでに撮影済みの何かをやりくりしているのではありません。
クリップは短く、動きは連続的に保ちましょう。24-30 fpsで3〜5秒のループはおよそ72〜150枚の元フレームを生成し、これはスクロールシーケンスに適した枚数のオーダーです(ステップ3で間引きます)。クリップ内でのハードカット、速いパン、強いモーションブラーは避けてください。動いている間は問題なく見えても、ユーザーがスクロールを途中で止めて1フレームで静止した瞬間にぼやけて見苦しくなります。
クリップを静止画にする方法は2つあります。Astorieのキャンバス上で、承認済みの動画をフレーム抽出ツールノードに送り、スクラブして個別のキービジュアルフレームを収集する方法——画像編集チェーンや次のショットの開始フレーム用に、少数のきれいな静止画が欲しいときに素早く使えます。スクロールスクラブ用に等間隔の完全なシーケンスが必要な場合は、クリップを書き出してFFmpegを実行します。これは開発者向けの検索結果が当然のように期待する事実上の標準的な方法です。
以下のコマンドは、元のフレームごとに連番のPNGを書き出します。-vf fps=30を使うと、クリップ本来のフレームレートに関係なく固定の秒間30フレームに再サンプリングでき、インデックスをゼロ埋めすることでファイルが正しい順序に並びます。まずPNGにすることで抽出を無劣化に保ち、次のコマンドでWebPに変換します。
PNGシーケンスをWebPに変換すると、スクロールスクラブのサイズであれば目に見える画質劣化なしにデータ量を60〜80%削減できます。実際にブラウザへ渡すのはWebP形式です——150フレームのPNGシーケンスは40MB以上になることがありますが、同じシーケンスを品質80のWebPにすると多くの場合8MB未満に収まります。cwebpでディレクトリ内を一括変換するか、FFmpegに直接WebPを出力させましょう。
完成したシーケンスは、Next.jsプロジェクトのpublic/frames/以下に配置し、安定してキャッシュしやすいパス(例:/frames/frame_0001.webp)から配信されるようにします。連番でゼロ埋めされたファイル名にしておけば、ステップ4で単純な文字列の埋め込みだけで任意のインデックスのURLを組み立てられます。

FFmpeg — extract a numbered frame sequence at 30fps
# Resample to a fixed 30fps and write zero-padded PNG frames
ffmpeg -i input.mp4 -vf "fps=30" frames/frame_%04d.png
# Variant: keep the clip's native frame rate (one PNG per source frame)
ffmpeg -i input.mp4 frames/frame_%04d.pngWebP conversion — shrink the payload 60-80%
# Convert every PNG in the folder to quality-80 WebP
for f in frames/*.png; do
cwebp -q 80 "$f" -o "${f%.png}.webp"
done
# Or have FFmpeg emit WebP directly (skip the PNG step)
ffmpeg -i input.mp4 -vf "fps=30" -quality 80 public/frames/frame_%04d.webpAI動画には甘いフレームが含まれます:モーションブラーのかかった中割りフレーム、時折発生する歪み、まれなアーティファクトなどです。シーケンスをリリースする前に、Astorieのキャンバス上にフレームを並べて選別しましょう——ぼやけたフレームを削除し、くっきりしたフレームを残し、ループの最初と最後のフレームが一致していてスクラブがきれいに繋がるかを確認します。150フレームの書き出しをきれいな90フレームまで間引くことで、ブラウザが事前読み込みするデータ量も軽くなります。
キービジュアルとなるフレームが画面いっぱいの幅で静止表示される場合——タイトル表示の瞬間や商品のクローズアップなど——は、4KにアップスケールしておくとRetinaディスプレイでもシャープさを保てます。選んだフレームをアップスケールツールノードに通し、結果をシーケンスに戻します。解像度の計算やユースケース別のガイドは、姉妹ガイド「画像を4Kにアップスケールする方法」を参照してください。単一フレームではなくクリップ全体をアップスケールしたい場合は「動画を4Kにアップスケールする方法」を参照してください。
参照品質のきれいなフレームは、スクロールスクラブ以外にも再利用できます:Nano Banana 2の画像編集チェーンに戻したり、次の動画ノードの開始フレームとして使い、次のショットに全く同じ見た目を引き継がせたりできます。これはクリップを延長する際に使うのと同じフレーム収集のテクニックです——最後のきれいなフレームを抽出し、次の画像から動画への生成の開始フレーム入力として使います(「動画クリップを延長する方法」を参照)。

ここからはコードの部分です。WebPシーケンスをImageオブジェクトの配列として事前読み込みし、drawImage()で現在のフレームを<canvas>に描画し、GSAP ScrollTriggerにスクロールの進捗をフレームのインデックスへマッピングさせます。ユーザーが縦に長い固定セクションをスクロールすると、インデックスが0から最後のフレームまで進み、canvasが再描画されます——これがスクラブの全てです。
シャープなスクラブと、ぼやけて残像の残るスクラブを分ける細部が2つあります。1つ目は、window.devicePixelRatioに合わせてcanvasを拡大し、Retina画面でもネイティブ解像度で描画されるようにしてからCSSでサイズを縮小することです——高DPI画面で論理ピクセルのまま描画すると画像がぼやけます。2つ目は、drawImage()のたびにctx.clearRect()を呼ぶ(またはcanvas全体を覆う不透明なフレームを描画する)ことです。そうしないと半透明のフレームが重なって残像が残ります。
以下のコードは、この仕組みを支える基本パターンです:render(index)関数、setupCanvas()ヘルパー内でのdevicePixelRatioによる拡大、描画のたびに呼ばれるclearRect、そしてonUpdateでself.progressを最も近いフレームにスナップさせるScrollTriggerです。frameCountと/frames/のパスは、ステップ2での書き出し内容に合わせて調整してください。
GSAP ScrollTrigger + canvas image-sequence scrub
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);
const canvas = document.querySelector("#sequence");
const ctx = canvas.getContext("2d");
const frameCount = 90;
const frames = [];
// High-DPI scaling: draw at native resolution, size down with CSS
function setupCanvas(w, h) {
const dpr = window.devicePixelRatio || 1;
canvas.width = w * dpr;
canvas.height = h * dpr;
canvas.style.width = w + "px";
canvas.style.height = h + "px";
ctx.scale(dpr, dpr);
}
setupCanvas(1280, 720);
// Preload the zero-padded WebP sequence from /public/frames
for (let i = 0; i < frameCount; i++) {
const img = new Image();
img.src = `/frames/frame_${String(i + 1).padStart(4, "0")}.webp`;
frames.push(img);
}
const state = { frame: 0 };
function render(index) {
ctx.clearRect(0, 0, canvas.width, canvas.height); // kill ghosting
ctx.drawImage(frames[index], 0, 0, 1280, 720);
}
frames[0].onload = () => render(0);
// Map scroll progress to a frame index
gsap.to(state, {
frame: frameCount - 1,
snap: "frame",
ease: "none",
scrollTrigger: {
trigger: "#sequence-section",
start: "top top",
end: "+=3000",
scrub: 0.5,
pin: true,
onUpdate: (self) => {
const i = Math.round(self.progress * (frameCount - 1));
render(i);
},
},
});スクロールスクラブの本質はプリロードの問題です。ブラウザは途切れなく再描画できるだけのフレームを取得する必要があるため、フレーム数はデバイスに応じて調整します:デスクトップではおよそ90〜150フレーム、帯域とメモリがより厳しいモバイルでは間引いて30〜60フレームです。スマートフォンにデスクトップ用のシーケンスを無理に読み込ませるのではなく、小さいビューポート向けには別途小さいWebPセットを配信しましょう。
プリロードは方向性を持たせ、段階的に行いましょう。最初の10〜20フレームを積極的に読み込んでセクションをすぐに操作可能にし、残りはバックグラウンドでストリーミングします。ユーザーが下にスクロールしていると分かっている場合は、均等にではなく現在のインデックスより先を優先的にプリロードします。対応している環境ではcreateImageBitmap()を使ってメインスレッド外でフレームをデコードし、drawImage()がスクロールをブロックしないようにします。
データ量の面で大きく貢献しているのがWebPです。品質75〜85であれば1フレームあたりのサイズは十分小さく、150フレームのデスクトップ用シーケンスでも1桁台のメガバイトに収まります。これによって、長い空白状態を作らずに積極的なプリロードが可能になります。それでもシーケンスが重く感じる場合は、画質を下げる前にフレーム数を減らしましょう——人間の目は、スクロール途中で止まった甘い低画質のフレームよりも、フレーム数が少ないことのほうがずっと許容できます。
GSAPを<video>に向けてスクロールからcurrentTimeを設定することもでき、開発機では問題なく見えます——しかし実機ではカクつきます。ブラウザは動画をフレーム精度でシークしません:currentTimeを設定すると最も近いキーフレームにスナップされるため、強く圧縮されたクリップをスクラブするとジャンプしたりガタついたりします。モバイルブラウザは自動再生ポリシーの下でプログラムによる再生を制限またはブロックすることもあり、スクロールのたびにキーフレームをデコードするのはコストが高くつきます。
canvas画像シーケンスはこの問題すべてを回避します。各フレームは独立した完全にデコード済みの画像なので、render(index)は構造上フレーム精度になります——47%までスクロールすれば、キーフレームへのスナップなしに、毎回正確にそのフレームが得られます。何も「再生」していないので自動再生ポリシーと戦う必要もありません。あなたはただ静止画を描画しているだけです。その代償が前もって必要なデータ量(フレームの事前読み込み)であり、これこそWebPとデバイス別のフレーム数調整が解決するものです。
だからこそ、スクロールスクラブアニメーションに関する開発者向けの検索結果は、こぞってcanvas方式を推しています。Astorieが貢献するのは上流工程です:生成・抽出・選別のステップを一手に引き受けることで、このcanvasパターンに渡すフレームは、既存ファイルから画面録画した何かではなく、きれいで、ループ可能で、必要な箇所は4Kになっているものになります。
ByteDance
Seedance 2.0は、クリーンな1080p出力によってフレーム抽出が実りあるものになる上流の動画モデルです——その各フレームは構図がよく整い、安定していて、画像編集の入力や静止画としての納品物として使えるだけの高いディテールを備えています。流れは次の通りです。承認済みのSeedanceクリップを生成または用意する→フレーム抽出のツールノードに通す→次のステップのために最も強いフレームを選ぶ、という順序です。よくある下流の用途としては、抽出したフレームをNano Banana 2やFlux Kontextの画像編集に参照画像として渡す、Sora 2やKling 3.0で異なるカメラワークの開始フレームとして使う、静止画のヒーロー画像として書き出す、などがあります。ツールのルーティングとパラメータについては、併設の`tools/frame-extraction`ページで詳しく解説しています。このハウツーは、Seedanceと組み合わせた流れに焦点を当てています。
Kling
Kling 3.0は、抽出したいフレームに人間や人型のパフォーマーが映っている場合に適した上流の選択肢です——その解剖学的に正確な身体・顔・衣服の動きは、同等の設定における他の動画モデルよりもはるかに、画像編集の入力や静止画の納品物として通用するフレームごとの静止画を生み出します。流れはSeedanceと同一です。承認済みのKlingクリップを生成または用意する→フレーム抽出のツールノードに通す→次のステップのために最も強いフレームを選ぶ、という順序です。Kling由来の抽出のよくある下流の用途としては、AIキャラクターシート(クリップ中盤の1フレームを抽出し、服装のバリエーションのためにNano Banana 2に渡す)、同じキャラクターを主役にした新しいカメラワークのショットの開始フレーム、演者の存在感が重要なブランドマーケティング向けのヒーロー静止画などがあります。併設の`tools/frame-extraction`ページではツールのルーティングを解説しています。このハウツーは、Klingと組み合わせた流れに焦点を当てています。
FFmpegを使ってクリップを連番の画像シーケンスに書き出します——ffmpeg -i input.mp4 -vf "fps=30" frames/frame_%04d.png——その後Web用にWebPへ変換します。Astorieのキャンバス上では、動画をフレーム抽出ツールノードに送り、キャンバスを離れることなくスクラブして個別のキービジュアルフレームを収集することもできます。
ffmpeg -i input.mp4 -vf "fps=30" frames/frame_%04d.pngを使います。-vf "fps=30"フィルターは、クリップ本来のフレームレートに関係なく固定の秒間30フレームに再サンプリングし、frame_%04d.pngはインデックスをゼロ埋めしてファイルが正しい順序でシーケンスに並ぶようにします。
canvas画像シーケンスはフレーム精度でカクつきがない一方、スクラブする<video>はそうではありません。動画要素のcurrentTimeを設定すると最も近いキーフレームにスナップされるため、圧縮されたクリップをスクラブするとガタつきます。また、モバイルの自動再生ポリシーがプログラムによる再生を完全にブロックすることもあります。独立した事前デコード済みのフレームを<canvas>に描画することで、正確で滑らかな制御が得られますが、その代償として事前のプリロードが必要になります。
デスクトップのスクロールスクラブではおよそ90〜150フレーム、モバイルでは間引いて30〜60フレームです。フレーム数が多いほど動きは滑らかになりますがプリロードは重くなるため、小さいビューポートにはより小さいWebPセットを配信し、データ量が重く感じる場合は画質を下げる前にフレーム数を減らしてください。
フレームを配列に事前読み込みし、onUpdateでself.progressをフレームインデックスに変換する——const i = Math.round(self.progress * (frameCount - 1))——ScrollTriggerを作成して、canvasをクリアしframes[i]を描画するrender(i)を呼び出します。セクションを固定してscrubを設定すれば、スクロール位置がそのまま再生位置にマッピングされます。
window.devicePixelRatioに合わせてcanvasを拡大します。canvas.widthとcanvas.heightをデバイスピクセル比で掛け合わせ、CSSのwidth/heightは論理サイズに設定し、ctx.scale(dpr, dpr)を呼び出します——これによりぼやけた論理ピクセルではなくネイティブ解像度で描画されます。また、半透明フレームによる残像を防ぐため、drawImage()のたびにctx.clearRect()を呼び出してください。
はい——それがAstorieの強みです。1つのノードベースのキャンバス上で、Kling、Seedance、Runwayなどのモデルを使い、開始・終了フレームの制御でループ可能なAIクリップを生成し、フレームを抽出・選別し、必要に応じてキービジュアルとなるフレームを4Kにアップスケールしたうえで、GSAP用にきれいなWebPシーケンスを/public/framesフォルダへ書き出せます。競合するチュートリアルはすでに手元にある動画ファイルから始まりますが、Astorieはパイプライン上流の生成・抽出・選別の工程を一手に引き受けます。