- はじめに
- テキストだと何が伝わらないのか
- スライドだとこうなる
- 動画にするとこうなる
- デモのシナリオと全体構成
- 作り方 1: シーケンス図はデータ駆動にする
- 作り方 2: 動きは「キーフレーム表」で書く
- 作り方 3: 名前付きタイムスタンプで同期を守る
- というか、書くのは生成 AI に任せられる
- 打ち合わせで何が起きたか
- まとめ
はじめに
私は普段、色々なお仕事をさせてもらっていますが、その中でも多いのが製造業関連のAI開発です。この分野では、「動くモノ」と「その裏で飛び交う通信」をセットで説明しないといけない場面がちょくちょくあります。
とあるロボット連携プロジェクトで、仕様の認識合わせに苦労した話です。

ロボットが動き回り、その裏では複数のシステムがせっせと通信し合う。こういう仕様を文章や図で説明しようとすると、なかなか伝わらないんですよね。
- ロボットが「どう動くか」は、平面図やイラストで説明できる
- システム間で「どんな通信が飛ぶか」は、シーケンス図で説明できる
- でも「こう動くとき、この通信が飛ぶ」という対応関係は、表現しづらい
この対応関係こそが仕様の肝なのに、資料の上では動きと通信がバラバラの紙に分かれてしまう。結果、読む人ごとに頭の中の再生結果が違ってきて、打ち合わせで認識のズレが発覚する…というのがよくありました。
そこで Remotion で「動き」と「通信」を 1 本の動画にまとめて共有してみたところ、打ち合わせがわりと円滑に進むようになりました。今回はその体験談をまとめます。
もちろん、最終的にはテキストと図に落とし込む必要はあるのですが、打ち合わせの場で認識を揃えるための補助輪として、動画で「動き」を見せるのが有効でした。
なお、実案件の中身をそのまま出すわけにはいかないので、記事中のデモは架空のシナリオで作り直したものです。
Remotion 自体の基本はシリーズ記事にまとめているので、初めての方はこちらからどうぞ。
テキストだと何が伝わらないのか
まずは動画にする前の状態から。仕様書に文章で書くと、こんな感じになります。
- 管理クラウドは配送ロボットへ配送指示(行先: 3F 会議室)を送信する
- ロボットは 1F 受付を出発し、エレベーター乗場に到着したらクラウドへ到着を通知する
- クラウドはエレベーター制御へ呼出リクエスト(1F→3F)を送信する
- エレベーター制御は号機を割り当て、応答を返す
- かごが 1F に到着したら、エレベーター制御はクラウドへ到着を通知する
- クラウドはロボットへ乗車許可を送信し、ロボットはかごに乗車する
- 乗車完了後、クラウドは行先階(3F)を指定する。かごは扉を閉めて昇降を開始する
- 3F 到着後、ロボットは降車して会議室へ移動し、受取人のスマホへ到着通知が送られる
内容としては正確です。ただ、これを理解するにはロボットの位置と通信のタイミングを頭の中で同時再生しながら読む必要があります。
関係者が 5 人いれば、5 通りの脳内再生が生まれます。そして後日の打ち合わせで「あれ、乗車許可って扉が開く前でしたっけ?後でしたっけ?」みたいなズレが発覚するわけです。
スライドだとこうなる
じゃあ図にしよう、ということでスライドにまとめると、だいたいこういう 2 枚に分かれます。


動きは 1 枚目、通信は 2 枚目。肝心の「対応関係」は、①〜⑥の番号と脚注でなんとか繋ぐしかありません。
シーケンス図も、配送 1 回分を 1 枚に収めようとすると 19 行。脚注に「※満員時のリトライは要確認」と小さく書いても、なかなか読まれません。ページを行ったり来たりしながら説明することになります。
動画にするとこうなる
同じ内容を動画にしたのが、冒頭に貼ったこちらです。全体で 70 秒ほど。
画面の左でロボットが動き、右のシーケンス図に通信が流れていきます。ポイントは 3 つです。
- 左のマップと右のシーケンス図が同じ時間軸で同期して進む
- シーケンス図には「いまどこを話しているか」を指す現在位置マーカーが出る
- 未決事項はその場で「❓要確認」バッジが出て、最後にまとめて再掲される
打ち合わせではこの動画を画面共有して、要所で一時停止しながら話します。「0:28 のここなんですけど」と言えば全員の頭が同じ場面になるのが、とにかく強いです。
デモのシナリオと全体構成
架空シナリオはこうです。オフィスビルで配送ロボットが 1F 受付から 3F 会議室へ荷物を届ける。ロボットは自分でエレベーターのボタンを押せないので、クラウド経由でエレベーターを呼んでもらいます。
登場するシステムは 4 つ。
| システム | 役割 |
|---|---|
| 配送ロボット | 荷物を積んでビル内を移動する |
| 管理クラウド | ロボットへの指示と、EV 連携の仲介役 |
| EV制御 | エレベーターの呼出・割当・行先階制御 |
| 受取人スマホ | 到着のプッシュ通知を受け取る |
動画は 6 シーン構成です。
| シーン | 内容 |
|---|---|
| タイトル | 動画の目的の提示 |
| 登場するシステム | ビル断面図と 4 システムの紹介 |
| STEP 1 | 出発 〜 EV 呼出 〜 乗車(❓満員時のリトライ) |
| STEP 2 | 昇降 〜 受渡し 〜 帰還(❓扉開放延長の制御主体) |
| ふりかえり | 要確認事項を宿題リストとして再掲 |
| エンド | 締め |
実装はシーンを <Series> で並べるだけです。シーンの尺は、後述する「タイムスタンプ表」から導出しています。
// Root.tsx(抜粋): シーン尺は timeline.ts の DUR だけを参照する const DeliveryDemo: React.FC = () => ( <Series> <Series.Sequence durationInFrames={DUR.title}> <TitleScene /> </Series.Sequence> <Series.Sequence durationInFrames={DUR.cast}> <CastScene /> </Series.Sequence> <Series.Sequence durationInFrames={DUR.seq1}> <Seq1Scene /> </Series.Sequence> {/* 以降 STEP2 / ふりかえり / エンド と続く */} </Series> );
ソースコードは全部で 1,300 行ほど。素材画像はゼロで、ビルもロボットも全部 SVG プリミティブで描いています。
制作は生成 AI と一緒に作って、テーマ検討も込み1時間ほどでした(このあたりは後述します)。
作り方 1: シーケンス図はデータ駆動にする
シーケンス図を毎回 SVG で組むのは大変なので、イベントの配列を渡したら描いてくれるコンポーネントを作りました。型はこれだけです。
export type SeqEvent = { t: number; // 出現フレーム(timeline.ts の T 表を参照する) from: string; to?: string; // 省略時は自分自身のアクション label: string; kind?: 'msg' | 'reply' | 'self' | 'note'; };
STEP 1 のシーケンスは、こういうデータになります。
export const SEQ1: SeqSpec = { participants: PARTICIPANTS, // 配送ロボ / 管理クラウド / EV制御 / 受取人スマホ events: [ {t: T1.ORDER, from: 'cloud', to: 'robot', label: '配送指示(3F会議室)'}, {t: T1.DEPART, from: 'robot', label: '受付を出発', kind: 'self'}, {t: T1.ARRIVE_HALL, from: 'robot', to: 'cloud', label: '乗場到着を通知'}, {t: T1.CALL_EV, from: 'cloud', to: 'ev', label: 'EV呼出(1F→3F)'}, {t: T1.EV_ASSIGN, from: 'ev', to: 'cloud', label: '割当応答(A号機)', kind: 'reply'}, {t: T1.NOTE_RETRY, from: 'cloud', to: 'ev', label: '満員時のリトライは?', kind: 'note'}, // ...乗車許可、乗車と続く ], };
kind で見た目を切り替えます。msg は実線矢印、reply は破線、self は鉤形のループ、note は付箋です。「まだ決まっていないこと」を note としてシーケンス図の中に置けるのがポイントですね。
描画側のロジックはシンプルで、フレームがイベントの t を過ぎたら、14 フレームかけて矢印を伸ばすだけです。
{events.map((e, idx) => { if (frame < e.t) { return null; // まだ発生していないイベントは描かない } const prog = interpolate(frame, [e.t, e.t + 14], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }); // prog に応じて矢印の先端座標を動かす })}
「いまどこを話しているか」の現在位置マーカーも、発火済みイベントを数えるだけで作れます。
// 発火済みイベント数から、マーカーの Y 座標を決める const fired = events.filter((e) => frame >= e.t).length;
作り方 2: 動きは「キーフレーム表」で書く
ロボットの移動は、[フレーム, x, y] の配列を補間するフックにまとめました。全文でもこれだけです。
const OPT = { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.inOut(Easing.ease), } as const; // [frame, x, y] のキーフレーム配列を折れ線補間するフック export const useKfXY = (keys: [number, number, number][]): XY => { const frame = useCurrentFrame(); const fs = keys.map((k) => k[0]); return { x: interpolate(frame, fs, keys.map((k) => k[1]), OPT), y: interpolate(frame, fs, keys.map((k) => k[2]), OPT), }; };
呼び出し側は「いつ・どこにいるか」を並べるだけ。同じ座標を 2 つのフレームに書くと「その場で停止」になるのが地味に便利です。
const robot = useKfXY([ [0, P.RECEPTION.x, P.RECEPTION.y], // 受付で待機 [T1.DEPART, P.RECEPTION.x, P.RECEPTION.y], // ← 同じ座標なので DEPART まで停止 [T1.ARRIVE_HALL - 6, P.HALL_1F.x, P.HALL_1F.y], // 乗場へ移動 [T1.BOARD, P.HALL_1F.x, P.HALL_1F.y], // 乗車許可が出るまで待機 [T1.BOARD + 55, P.CAB.x, P.CAB.y], // かごに乗り込む ]);
作り方 3: 名前付きタイムスタンプで同期を守る
ここが一番の学びでした。
最初は「マップはフレーム 380 で動かして、シーケンス図は 380 で矢印を出して…」と、両方に生の数字を手打ちしていました。これが尺を調整するたびに崩れるんですよ。マップだけ直してシーケンス図を直し忘れる、の繰り返しで心が折れました。
そこで、時間に関する数値を 1 ファイルに集約して、全部に名前を付けました。
// timeline.ts: 時間に関する数値はこのファイルにしか書かない export const T2 = { BOARDED: 30, // ロボ→クラウド: 乗車完了を通知 SET_DEST: 80, // クラウド→EV: 行先階を指定(3F) DOOR_CLOSE: 180, // かご: 扉が閉まる LIFT_START: 210, // かご+ロボ: 1F→3F 昇降開始 ARRIVE_3F: 340, // かご: 3F 到着(扉が開く) // ... END: 640, } as const; export const DUR = { title: 120, cast: 360, seq1: T1.END, // シーンの尺も T 表から導出する seq2: T2.END, // ... } as const;
マップ上の動きも、シーケンス図のイベントも、ぜんぶこの表を参照します。たとえば STEP 2 の昇降シーンでは、エレベーターのかごとロボットが同じキーを読んで動きます。
// かごの高さも、中のロボットの位置も、同じ T2 のキーを参照する const cabTop = useKf([ [T2.LIFT_START, cab.topAt1F], [T2.ARRIVE_3F - 6, cab.topAt3F], ]); const robot = useKfXY([ [T2.LIFT_START, P.CAB.x, P.CAB.y], [T2.ARRIVE_3F - 6, P.CAB.x, P.HALL_3F.y], // 以降、降車 → 会議室 → 帰還 ]);
同じ入力範囲・同じイージングで補間されるので、かごとロボットは構造的にズレようがありません。シーケンス図の t も同じ表なので、マップと図の同期も保たれます。
仕様変更で「扉が閉まるタイミングを 1 秒遅らせたい」となったら、T2.DOOR_CLOSE を直すだけ。矢印もテロップもバッジも全部ついてきます。
というか、書くのは生成 AI に任せられる
ここまで「作り方 1〜3」と説明してきましたが、正直に言うと、このコードを全部手で書く必要はもうありません。このデモ自体、Claude Code に指示を出して作ったものです。
出した指示は、だいたいこのくらいの粒度です。
ビルの断面図の左でロボットが動いて、右のシーケンス図が同期して進む動画を作って。 シナリオは配送ロボットとエレベーターの連携。 まだ決まっていない仕様は「要確認」バッジをその場に出して、最後にまとめて再掲してほしい。
これで大枠のコードが出てきて、あとは対話しながら「扉は半透明にして中のロボットを見せて」「バッジの位置をずらして」と詰めていくだけでした。
しかも Remotion は公式に AI エージェント向けの Agent Skills を提供しています。
npx remotion skills add
これを入れておくと、Claude Code などのコーディングエージェントに Remotion の作法(Composition の組み方や interpolate の使い方など)を教え込めるので、生成されるコードの精度が上がります。詳しくは 公式ドキュメント を参照してください。
人間がやるのは、シナリオを考えることと、タイムスタンプ表の数値調整、あとはプレビューや remotion still での見た目確認くらい。「動画がコードである」ということは、AI に書かせやすく、差分で直しやすいということでもあるんですよね。
打ち合わせで何が起きたか
実案件でこの形式の動画を共有してから、打ち合わせが変わりました。
1. 質問が具体的になった
「この通信のタイミングって大丈夫でしたっけ」ではなく、「0:33 の乗車許可って、扉が開いた後で合ってますか?」のように、シーンを指した質問が出るようになりました。土台の認識が揃っているので、議論がいきなり本題から始まります。
2. 「要確認」に議論が集中した
動画の途中で ❓ バッジが出る箇所は「ここはまだ決まっていません」の宣言です。流し見していても目に入るので、そこで自然に議論が始まります。最後のふりかえりシーンが、そのまま次回打ち合わせの宿題リストになりました。
3. 認識のズレが早期に見つかった
文章では読み流されていた箇所も、動きとして見せると「あれ、想定と違う」が一瞬で出てきます。実装が進む前にズレを潰せたので、手戻りが目に見えて減りました。
4. 更新して再共有するのが楽
仕様が変わったら、コードを直して npx remotion render して再共有するだけです。動画なのに Git で差分レビューできるのは、コードで動画を書く Remotion ならではですね。
まとめ
| 課題 | 動画での解決 | 使った仕組み |
|---|---|---|
| 動きと通信の対応が伝わらない | マップとシーケンス図を同じ時間軸で並べる | 名前付きタイムスタンプ表 |
| 「いまどこの話か」が揃わない | 現在位置マーカー + 一時停止して指差し | 発火済みイベント数から位置決め |
| 未決事項が流される | ❓要確認バッジ + 最後に再掲 | spring バッジ + ふりかえりシーン |
| 仕様変更に資料が追従しない | コード修正 → 再レンダリング | 時間の単一情報源化 |
動画といっても、やったことは「イベントの配列」と「キーフレームの表」を書いただけで、After Effects 的な職人技はどこにも出てきません。React が書ければ作れます。
というより、生成AIに適切な指示を出せれば、ほとんど自動で作れます。Remotion は「動画をコードで書く」だけでなく、「AIに動画を作らせる」ためのフレームワークでもあるんですね。
仕様書を置き換えるものではなくて、仕様書の読み合わせを不要にする補助輪くらいの位置づけがちょうどいいと感じています。認識合わせに苦労しているテーマがあったら、1 本作って持っていくと打ち合わせの空気が変わるはずです。
次はナレーション音声を載せて、非同期でも伝わる形にするのを試したいなと思っています。