プレビューを書き出しに一致させるため、2つ目のレンダリングエンジンを出荷した。かえって事態は悪化した。
私のAIクリップエディターには、プレビューと書き出しを一致させるため2つのレンダリングエンジンがありました。2つのエンジンはずれていく。なぜ片方を消し、FFmpegの単一パイプラインに作り直したのか、その理由を書きます。
2026年9月3日
KattoはAIを使った動画クリッパーです。長い動画を放り込むと、いい瞬間を縦型のショート動画に切り出してくれます。そしてエディターで、書き出す前にフレーミングやキャプション、レイアウトを直せます。私はこれを一人で、build in public(公開しながらの開発)で作っています。この記事は、そのエディターで私がやらかした失敗と、それを直す過程で「見たままが手に入る(WYSIWYG)」について学んだことの話です。
解決しようとしていた問題
クリップエディターの成否は、たった一つの約束にかかっています。プレビューで見えているものが、そのままダウンロードされる。書き出したときにキャプションが1ピクセル上にずれていたり、クロップが動いていたり、色が違っていたりすれば、ユーザーはツールを信用しなくなります。そして、信頼こそがこのプロダクトそのものなのです。
私のプレビューは、ブラウザ内でレンダリングされるReactのコンポジションです。一方、書き出しはサーバー側でFFmpegを使ってレンダリングしていました。同じクリップを、2つの異なるレンダラーが描画していたわけです。案の定、両者はずれていきました。キャプションのフォント、絵文字の描画、スプリットスクリーンの正確なクロップ位置。まさに定番の「プレビューと書き出しが一致しない」問題です。
正しく感じた(が、間違っていた)解決策
そこで私は、いかにも当然に見える手を打ちました。ブラウザのプレビューを描いているのと同じReactコンポジションを、ヘッドレスChromium(Remotion)経由でサーバー側の書き出しにも使うようにしたのです。コンポーネントは一つ、真実の源(source of truth)も一つ。これでプレビュー=書き出しが保証される、はずでした。
これはRemotionを貶しているわけではありません。Remotionは本来の用途において素晴らしいツールです。間違えたのは私です。既存のFFmpegパイプラインの隣に、2つ目の書き出しエンジンとしてこれをくっつけてしまったのです。
最初のクリップ、つまりエディターを開く前にAIが生成するクリップは、依然としてFFmpegでレンダリングされていました。速いし、実績があるし、パイプライン全体を回してくれる。これを引っこ抜くつもりはありませんでした。
こうして私は、ファイルを生成するエンジンを2つ抱えることになりました。生成クリップはFFmpeg、編集後の再書き出しはChromiumとRemotion。消し去ろうとしていたはずの食い違いが、かえって悪化したのです。結果一覧で見ているクリップ(FFmpeg)と、ちょっと編集して再書き出しした同じクリップ(Remotion)が、微妙に違う仕上がりになりうる。私はレンダラーを1つから2つに増やしておいて、それを「一致(parity)」と呼んでいたわけです。
おまけに、Chromiumのレンダリングは遅かった。1クリップあたり3〜4分、FFmpegの約30秒に対してです。しかも時々失敗しました。失敗すると、こっそりFFmpegにフォールバックし、ユーザーには編集内容が黙って落とされたファイルを渡していました。最悪の失敗です。間違っていて、しかも静かに。
気づいたこと
「プレビュー=書き出し」は、プレビューそのものをレンダラーにすることで達成されるのではありません。レンダリングの真実の源を一つに保ち、プレビューをその忠実な近似にすることで達成されるのです。
ブラウザのPlayerは、優れたプレビューです。それを2つ目の書き出しエンジンにしてしまったのが間違いでした。そうなった瞬間から、同じジオメトリの実装が2つ存在し、変更のたびにずれていくことになったのです。
やったこと
- ファイルを生成するエンジンはFFmpeg一つに統一。生成も再書き出しも、同じパイプラインを通ります。典型的な60秒未満のクリップの書き出しは、Chromiumパスでの3〜4分から、約30秒(実測で32s)にまで落ちました。
- ブラウザのPlayerは、得意なこと、つまりインタラクティブなプレビューに戻しました。出荷されるものは一切レンダリングしません。
- ブラウザにしかできなかった装飾をFFmpegに移植しました。単語ごとに色が循環するキャプションと、スタックレイアウトの調整可能な分割比率です。これで高速パスがよくあるケースをカバーできます。
- 静かなフォールバックは廃止しました。忠実にレンダリングできない場合、ユーザーには目に見えるエラーが表示されます。黙って間違ったファイルを渡すことは、二度とありません。
- 座標系を一つに、これが最後に仕上げているピースです。プレビューでクロップをドラッグしたとき、その正確な矩形こそがFFmpegの使うべきものであって、サーバーが独自に再計算した別のジオメトリであってはなりません。これは微妙なやつです。エンジンを統一したあとでさえ、プレビューはあるやり方でクロップ矩形を計算し、書き出しは別のやり方で計算していました。いま私はこれを片付けているところで、FFmpegがエディターの状態にある正確な矩形をそのまま消費するようにしています。というのも、教訓は一段下のレベルでも同じだからです。同じものを2度計算すれば、それが必ずずれの元になる、と。
教訓
- 「プレビューをレンダラーにする」は魅力的だが、たいてい間違い。他に何かレンダリング経路を持った瞬間、レンダラーが2つになってしまいます。出力の真実の源は一つ。プレビューはそれを近似するもの。
- 「一致のための」2つ目のエンジンは、隠れた3つ目のずれの源になる。2つのコードパスが同じジオメトリやフォーマットを計算すれば、必ずずれます。問題は「いつ」かだけです。
- 静かなフォールバックは最大の罪。警告もなく微妙に間違ったファイルは、正直なエラーよりも多くの信頼を失わせます。失敗するなら大きな声で。
- ログではなく、成果物を検証せよ。私のログは「スタックレイアウトをレンダリング済み」と言っていました。本当のバグを見つけられたのは、ストレージから実際に書き出されたMP4を引っ張り出して、1フレームを目で見たときだけでした。ピクセルこそが真実です。
私は一人で、速く、公開しながら出荷しているソロファウンダーです。今回の件は、善意から出た回り道という代償を払わせてくれました。もしあなたがプレビューと書き出しを持つ何らかのエディターを作っているなら、信頼への最短ルートは退屈なものです。レンダラーは一つ、正直なエラー、そしてフレームを確認すること。
Kattoはkatto.techにあります。動画ツールを作っている方がいれば、プレビューと書き出しの一致について、ぜひ知見を交換したいです。