stopmotion-video について
音楽に同期したストップモーション風動画を作るスキルです。拍や小節に沿って画面を構成し、Remotionを中心とした制作手順とルールを示します。
主な機能
- 01八分音符ごとのポーズ設定
- 02小節線に合わせたページ変更
- 03クリック音と画面変化をドラムに同期
- 04Remotionによるレンダリングと確認
活用シーン
- 01音楽に同期したローンチ動画の制作
- 02曲のフレーズに沿ったシーン構成
- 03メトロノームによるポーズ時刻の確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ kosta4a/ stopmotion-video --skill stopmotion-videoインストール後、Claude Code で/stopmotion-videoを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
assets/remotion/beat.tstransitions.tsxreferences/checking.mdclock.mdhits.mdlive.mdposes.mdskipping.mdsong.mdsound.mdtype.mdscripts/duck.mjsfind-tempo.mjsmake-metronome.mjs詳細
ストップモーション動画
このスキルで作る動画は、曲に合わせて撮影したストップモーションのように動きます。それぞれのポーズを8分音符1つ分保持し、その後、すべてが一斉に切り替わります。ページは小節線で切り替わります。クリック音はすべてドラムのヒットに合わせ、大きな変化は大きなキックに合わせます。フレームレートは意図的に低く保ちます。動きがぎこちなく見えるときは、ポーズの間隔を調整します。ビートを滑らかにすることは決してありません。
このルールは、169.825 BPMのドラムンベースのトラックに合わせて編集した47秒のローンチ映像を、数百回にわたって音符を確認しながら制作した経験から生まれました。映像は30fpsで、8分音符ごとに1つのポーズを保持していました。これは1秒あたり5.66ポーズ、つまり1ポーズあたり5フレームまたは6フレームです。ここに記載する値は映像のもので、live.mdには、その映像のシーンをメトロノームに合わせて再生するウェブページの値を記載しています。ルールがこれらの値を超えて一般化できる場合は、その旨を本文に記載します。
用語
- ポーズ:保持される1枚の画像で、曲の8分音符に相当します。コードではビート(
STEP_MS、useBeat)と呼びます。 - 曲のビート:BPMが数える単位で、4分音符に相当します。ポーズ2つ分の長さで、メトロノームが1回クリックします。
- 小節:曲のビート4つ分、ポーズ8つ分です。ドラムンベースでは、0から数えてポーズ0でキックが鳴り、ポーズ2と6でスネアが鳴ります。
- ヒット:曲中のドラム音です。大きなキックはフレーズの始まりを告げるキックです(この映像のトラックでは奇数小節ごとに鳴ります)。
- コード上のms:コード内の時間です。200msのポーズを基準に記述され、
SPEED倍の速さで再生されます(SPEED = TEMPO / 150)。 - タイムスタンプはm:ss.ff形式で記述します。ffはフレーム数を表します。これはRemotion Studioで時間を表示する形式であり、メモを記録する形式でもあります。
制作方針
- 低いフレームレートを意図的に保ちます。 動きを直すためにビートを滑らかにすることは決してありません。代わりに、その動きのポーズの間隔を調整します。
- すべての要素は1ビート分ポーズを保持し、一斉に切り替わります。 トランジションも例外ではありません。埋め込みアニメーションやスクロールするページもポーズに合わせて進むため、独自のタイミングで動くものはありません。例外はタイピングだけです。タイピングは、実際のタイピングと同じように、ポーズからビートより速く進みます。
- 曲を時計として使います。 ストーリーボードを作る前に曲のタイミングを測ります。ページは小節に合わせて配置し、アクションはヒットに合わせます。
- 動画全体で時計は1つだけです。 ポーズはシーンではなく、動画のフレーム上で数えます。
- 小節単位で動かします。 ページの長さは小節単位で変更するか、次の小節線までに別の場所でポーズ数を戻します。そうしないと、それ以降のすべてが曲とずれてしまいます。
- 何もしないポーズは作りません。 ページ上で起こることが終わったら、視聴者が内容を把握するのに必要な長さだけ保持します。
- 変化の種類は少なくし、方向は1つに絞り、フェードは使いません。
- 測定して確認します。 1ポーズにつき1枚の静止画、レンダリングの音声から測定したタイミング、メトロノームに合わせたカットを使います。目視で同期を判断しないでください。
デフォルトのツール:Remotion
プロジェクトですでに別の方法で動画を制作していない限り、動画制作には Remotion を提案します。ストーリーボードを作る前に、その理由とともに伝え、ユーザーから別のツールを求められた場合にのみ変更します。ウェブページでは、代わりにライブ再生します(live.md)。モーションツールではルールは同じですが、ヘルパーは使えません。
- 適している理由。 各フレームはフレーム番号から計算される React コンポーネントなので、ポーズは配置されたフレーム上で正確に表示され、毎回同じようにレンダリングされます。レンダリングではフレームが複数のタブで順不同に描画されるため、フレームに基づいて動作する動画だけがこれに対応できます。
assets/remotion/のヘルパーは Remotion 用に作られており、Studio では時間が m:ss.ff 形式で表示されます。これはノートが届くときの形式です。 - セットアップします。 空のテンプレートを使って
npx create-video@latestを実行します。remotionとすべての@remotion/*パッケージを同じバージョン(この映像では 4.0.532)に固定し、TypeScript は 5.x を使用します(clock.md §7)。コンポジションは 1 つとし、解像度を 1920×1080、フレームレートを 30fps にします。beat.tsとtransitions.tsxをsrc/にコピーして、TEMPOを設定します。 - 設定します。
remotion.config.tsで、JPEG フレームの品質を 95 にします(デフォルトの 80 では、フラットな背景や細い文字の輪郭が粗くなるためです)。色は BT.709 に設定します(プレーヤーでブラウザーと同じ色が表示されるようにするためです)。上書きも有効にします。 - プレビューとレンダリング。 作業には
npx remotion studioを実行します。1080p の場合はnpx remotion render <Comp> out/<name>.mp4を実行し、4K の場合は--scale=2を指定します。範囲やポーズを確認するには、--frames=<first>-<last>またはnpx remotion still <Comp> --frame=<n>を使用します。
決定の順序
上から順に進めます。各ステップが次のステップを制約します。
- ツールを提案します。 プロジェクトですでに別の方法で動画を制作している場合を除き、Remotion を使います。ほかのことを始める前に、上記のとおりセットアップします。
- 曲を測定します。 オンセットからテンポを見つけ、使用する区間で拍子のグリッドが維持されることを確認します。小節とフレーズ、ドラムが抜けて戻る箇所、強いキックに印を付けます。フレーズ最初のキックのわずか(23ms)手前で、サンプル単位で正確に抜粋します。song.md を参照してください。
- 時計を設定します。 1つのポーズは曲の8分音符です。
SPEED = TEMPO / 150を使って200msのポーズで作成し、30fpsでレンダリングし、動画全体のフレーム上でポーズを数えます。clock.md を参照してください。 - ページを小節に合わせます。 ストーリーボードをポーズ単位で書きます。各ページはポーズの整数個分で構成し、小節線から始めます。アニメーションを始める前に、曲のイベントをポーズに割り当てます。
- ヒットを配置します。 コードは8分音符に合わせてチャンク単位で入力し、タイトルと押しっぱなしのバックスペースは、それぞれのポーズからビートより速く進めます。クリック音とポップ音はスネアに、着地と新しいページはダウンビートに、重要な変化は強いキックに合わせます。ビルドアップは静かな箇所で行い、ドラムが戻るタイミングで着地させます。hits.md を参照してください。
- ポーズの間隔を設定します。 各カーブをポーズ単位で選びます。動きの最初のポーズが最初のステップであり、ポーズごとに目に見える動きが必要です。動きはじわじわ進むのではなく、着地させます。poses.md を参照してください。
- ページを切り替えます。 ビートに合わせたハードカット、地面のカット、シートのように重ねるページ、カメラが色の中へ入る演出、内側からの塗りつぶし、形を通して開く演出、またはピクセルブロックを使います。フェードは使いません。hits.md を参照してください。
- 文字を設定します。 サイズが大きくなるほど字間を詰め、それに合わせて行間も詰めます。すべてのポーズで、すべての単語と背景とのコントラスト比を4.5:1以上にします。タイトルはフェードではなく、入力して表示します。type.md を参照してください。
- サウンドを作ります。 クリック音はすべて曲そのもののヒットにサンプル単位で正確に配置し、クリック音全体を曲と同時に始まる1つのトラックにまとめます。クリック音の下で音楽の音量を下げ、確認用にメトロノームをカットしたものも用意します。sound.md を参照してください。
- ビートを飛ばす箇所を決めます。 ドロップ、ダッキング、ホールド、スナップ、丸ごとの小節のカット、曲のビートで終わる演出を使います。skipping.md を参照してください。
- 確認します。 コンタクトシートを作り、同期を測定し、メトロノームをカットしたものを確認します。タイムスタンプをポーズに対応付けてメモします。checking.md を参照してください。
- ブラウザーでライブ再生しますか? メトロノームに合わせて同じルールを適用します。ページの時間に基づく時計を1つ使い、映像を音声より25ms先行させ、音声はオーディオクロック上で先行してスケジュールし、曲がない場合はすべての変化をクリック音に合わせます。live.md を参照してください。
ルール一覧
時計
| 項目 | ルール |
|---|---|
| フレームレート | 意図的に30fpsにします。滑らかな60fps版でも同じストーリーボードを使えます(clock.md §8) |
| ポーズ | 曲の8分音符:169.825 BPMでは176.65ms、1秒あたり5.66ポーズ、30fpsでは5フレームまたは6フレーム |
| 作成 | すべての時間を200msのポーズ単位で指定します。SPEED = TEMPO / 150、フレームへの変換にはms()、埋め込み時計にはseconds()を使います |
ポーズnの最初のフレーム | 動画全体のフレーム上でceil(n × frames per pose)とします(beatFrame) |
| ページ | ポーズの整数個分で構成し、小節線から始めます(ポーズが8で割り切れること) |
| 長さの変更 | 小節単位で変更するか、次の小節線より前のポーズを戻します |
| Studioでのドラッグ | トラックをドラッグすると、映像を音声に対してずらすfromオフセットが書き込まれます。これを削除します |
間隔
| 動き | イージング | ポーズ |
|---|---|---|
| 出入り(カメラの動き、パス、下線) | easeInOutSine | 6 ポーズにわたり、7、18、25、25、18、7% |
| フレーム外から入る | イーズアウト | そうしないと、遅い開始が見えないところで進んでしまいます |
| 別のページに重ねるページ | easeOutSine | 3 ポーズのトランジションで38、70、92% |
| ズーム、線を太くする、開口部を広げる | 各ポーズで同じ係数ずつ拡大 | ズームの対象は、中点まで直線上を移動させます |
| ポーズ数が少なくない場合 | キュービックのイーズイン・アウト(3、12、35、35、12、3:中盤で大きく進みます)。強いイーズアウト(最初のポーズに着地します:3 ポーズのトランジションでは78、97、100%、moved() を使う動きでは88、99、100%) |
ビートに合わせるもの
| 対象 | ルール |
|---|---|
| クリック | キックかスネアに合わせ、押された状態は1ポーズ。最後のクリックの次のポーズでカーソルを消します |
| キャレット | 入力中は表示し、その後は2ポーズ表示、2ポーズ非表示にします |
| スピナー | 1ポーズで最大4分の1回転。1ポーズで半回転を超えると、逆回転に見えます |
| 転がるマーク | 移動距離を半径で割った分だけ回転し、easeOutSine で直立した状態に着地します |
| カウンター | 数字は強いイーズアウトで1ポーズに表示し、タブラー数字を使います。切りのよい数字では2ポーズ表示します |
| タイトル | ポーズからビートより速く入力します。3フレームごとに1単語、長い行は25msごとに1文字(手作業で設定)です |
| コード | 1ポーズごとに2~4文字の不規則なまとまりで入力します |
| バックスペースを押し続ける | 1回の動作で、25msごとに1文字(手作業で設定)消します。改行も含みます |
| カメラの上昇 | 上昇し、頂点で2ポーズ完全に静止してから下降します |
| フレーム外から入る | 動き始めるまで描画しません |
| 手作業で配置 | そのポーズの移動量の10分の1以内のずれに収め、停止後はずれをなくします。数ピクセル以内で一度だけ下方向に動かします |
ヒット
| 曲の中の要素 | 映像での要素 |
|---|---|
| 8分音符 | まとまりごとのコード入力、動きの各ステップ |
| スネア | クリック、ポップ |
| ダウンビート | 着地、新しいページ、ページの最初の動作 |
| 大きなキック | 大きな変化:ページの着地、最後のページが完成する瞬間、最後のクリック |
| ドラムが止む | 盛り上がり |
| ドラムが戻る | 着地 |
クリックはビートに合わせる必要があります。トランジションはビートからずれてもかまいません。
サウンド
| 項目 | ルール |
|---|---|
| クリックの配置 | サンプルに合わせ、曲のヒットに合わせます(映像のヒットはポーズから25ms遅れていました) |
| クリックのファイル | ティックの前に40msの無音を入れ、サンプルに合わせてトリミングしても音が欠けないようにします |
| クリックトラック | すべてのクリックを、曲と同時に始まる1つのトラックにまとめます |
| ダッキング | 重要なクリックの周囲は無音にし、それ以外のクリックの下では160msにわたり45%に下げます。フェードは10msです |
| メトロノーム | 曲の各ビートのヒットにクリックを入れ、小節の最初は音を高くし、音楽は20%にします。書き出しには含めません |
文字
| サイズ | トラッキング | 行送り |
|---|---|---|
| 20px 未満 | 0。モノスペースの大文字は+2% | 1.4~1.5 |
| 20px 以上 | −1% | 約1.4 |
| 32px 以上 | −2% | 約1.25 |
| 56px 以上 | −3% | 1.1~1.2。2行のディスプレイタイトルでは0.9まで下げます |
すべての単語は、どのポーズでも背景とのコントラスト比を少なくとも4.5:1にする必要があります。デフォルトでは1つのファミリーを使います(例ではGeistとGeist Mono)。tactile-designと同様に、白地に黒を使います。映像自体ではディスプレイ用書体と本文用書体を組み合わせ、各ページの背景には単色を使っていました。カウンターにはタブラー数字を使います。タイトルはフェードインではなく、入力して表示します。
ブラウザーでのライブ再生
| 項目 | ルール |
|---|---|
| 時計 | beat = anchorBeat + elapsed s × BPM / 60 × 2 を performance.now() で計算します。テンポ変更時は現在時刻を基準に再設定します。各シーンはビートの純粋関数です |
| 画像 | 25ms先のビートを読み取ります。各シーンはアニメーションフレーム内でコミットします(flushSync) |
| サウンド | 25msごとのタイマーで、オーディオクロック上の次の120msをスケジュールします(getOutputTimestamp())。ウィンドウはオーディオ時間で計測し、出力レイテンシを加算します |
| 表示中 | 画面上にあるものだけをアニメーションさせます。画面に最も多く表示される例の音が聞こえます |
| 開始 | 読み込み時に試し、その後は最初の押下またはキー入力時に試します。サウンドを開始させた押下ではミュートになりません(600ms)。ミュート状態は記憶されます |
| 曲なし | クリックごとに変化します。動きは8分音符単位で進められますが、開始と着地はクリックに合わせます。そのクリック自体のメトロノーム音は鳴らしません |
| メトロノーム | 静かな木のノック音です(50msの880Hz音、各小節の最初は1320Hz)。音楽がないときは低音を鳴らさず、すべてを1つのリミッターに通します |
| 一時停止 | 最も近いクリックで行います。そこまで再生するか、160msかけて戻します |
| モーションの軽減 | アイデアが伝わる静止ポーズにし、サウンドボタンを押した後だけ動かします |
| 文字 | 単語は4.5:1、意味のあるマークは3:1、拡大縮小されたシーン内の最小文字は、描画時に9px以上です |
ビートを飛ばすとき
グリッドは厳密なので、そこから外れるのは意図的な判断です。以下の各例外には、skipping.mdに理由が記載されています。
- 重要なクリックの前後で音楽を止めます。 クリックの50ms前から次の小節線まで無音にし、10msのフェードをかけます。クリックだけが聞こえ、その後に続く音とともにビートが戻ります。
- ほかのクリックの下で音楽を小さくします。 クリックの低音が聞こえるよう、160msの間、音楽を45%まで下げます。
- 次の動きが始まる前に、結果だけを曲の1ビート分見せます。 たとえば、コピー後のチェックマークです。
- 上昇の頂点で、同じポーズを2回保ちます。
- 丸い数字の位置で、2つのポーズを保ってから先に進みます。
- ビートの間に入る音符を、最も近いアクセント付きビートに合わせます。
- 音符がより短い長さを求めていても、小節全体をカットします。その代償を説明します。
- 曲のビートで終わります。 最後のページを約2秒表示し、最後のポーズの間に音楽をフェードアウトします。それ以外の場合は、動作の直後に終わります。
絶対にリリースしないこと
- 間隔の悪い動きを隠すために、ビートから外して動きを滑らかにする
- シーンが最初のフレームからポーズを数える
- 理由を横に記載せず、小節線以外から始まるページ
- 小節単位ではない長さの変更、または次の小節線までに元に戻されない長さの変更
- Studio の
fromオフセットをコンポジションツリーに残す - キックやスネアとずれたクリック音、またはサンプルではなくフレームに配置したクリック音
- 短いクリック音を、単一のベイク済みトラックではなく、それぞれのフレームで開始する
- 最初のポーズが離れる前のポーズと同じ動き、または最後の数pxをじわじわ動かす動き
- フレーム外から来るものが、動き出す前に描画される
- 1ポーズで半回転を超えて回るスピナーやホイール
- フェード、または彩度の高い背景同士のクロスフェード
- 1秒あたり3回を超える背景の切り替え
- いずれかのポーズで、背景に対するコントラスト比が4.5:1未満の文字
- ディスプレイ用文字のトラッキングを0のままにする、または比例幅の数字でカウンターを表示する
- ページの動作が終わった後の待機ポーズ、または「理由なく延びる」余韻
- 書き出しにメトロノームを含める
- レンダリングで CSS トランジションやアニメーション、またはライブラリの
animate()を使う - ポーズごとに確認し、曲に照らして計測していない
- ライブ:フレーム上でサウンドを再生する、または出力レイテンシを含めない先読み
- ライブ:クリック後に画像を描画する、またはほかのシーンより1フレーム遅れてシーンをコミットする
- ライブ:最初の押下でサウンドを開始すると同時にミュートする、またはモーションの軽減が有効なときに、求められていないのにシーンを動かす
参考資料
作業中の手順に対応するファイルを開いてください。それぞれの項目では値とその理由が対になっており、新しい曲にも適用できます。
- song.md:トラックの計測、抜粋部分の切り出し、ポーズと小節への割り当て。
- clock.md:単一のクロック、ポーズごとのフレーム数、小節線、ページの長さ、Remotion との接続、60fps のバリエーション。
- poses.md:ストップモーションの間隔と、各種要素に関するルール。
- hits.md:各要素が配置される位置と、トランジション。
- skipping.md:ビートを飛ばす、保つ、落とす、またはダッキングするタイミングと、それぞれの理由。
- sound.md:サンプルに合わせて配置するクリック音、ダッキング、クリックトラック、メトロノーム。
- type.md:フレーム内の文字。文字サイズ、トラッキング、行送り、コントラスト、文字の入力方法。
- checking.md:コンタクトシート、同期の計測、タイムスタンプとしてのメモ、レビューループ。
- live.md:ブラウザーでメトロノームに合わせてライブ再生する方法。同じルールを使い、ページのクロック、事前に予約するサウンド、サウンドの開始と一時停止、モーションの削減、画面上の文字を扱います。
ツール:
assets/remotion/beat.ts:クロックとビートのヘルパー(TEMPO、SPEED、ms、useBeat、moved、clicked、press、blink、曲線)。assets/remotion/transitions.tsx:onTheBeat、inSteps、onQuarters、slideOver、pixels、throughShape。scripts/find-tempo.mjs:WAV ファイルから曲のテンポ、グリッド、ドリフト、小節を調べます。scripts/duck.mjs:クリック音の周辺で音楽を消音または音量を下げます。scripts/make-metronome.mjs:確認用のクリックトラックを作成します。