MMDの再生画面を動画・静止画で書き出す(ブラウザだけで)

作ったモーションを人に見せるには、動画か静止画にするのが確実です。ブラウザだけで完結させられますが、素朴に録画すると環境によってスローモーションの動画ができます。 何が原因で、どう避けるかをまとめます。

最終更新: 2026-09-13 · GPU無しのソフトウェアラスタライザ環境で実際に書き出して確認。透過の検証はcanvasの四隅と中央のアルファ値を読んだ実測

画面録画とフレーム単位の書き出しは別物

画面録画(MediaRecorder)フレーム単位(WebCodecs)
撮り方実時間で流しながら撮る再生ループを止めて1コマずつ描く
描画が遅い環境スローモーションになる指定どおりの長さ・fpsになる
音声入る(BGMも一緒に録れる)入らない(映像のみ)
対応状況広いVideoEncoder がある環境のみ

画面録画は「今画面に出ているもの」をそのまま記録します。 1フレームの描画に50ミリ秒かかる環境で30fpsの動画を録ると、実時間は進むのに描けたコマが足りず、動きがゆっくりになります。MMDの再生はモデル1体でも重いので、これは珍しい状況ではありません。

フレーム単位の書き出しは、再生ループを止めて「固定のΔtで1コマ進める → 描く → エンコーダへ渡す」を繰り返します。 描画に何秒かかっても、タイムスタンプは自分で振るので結果は変わりません。書き出しに実時間の何倍かかっても、出てくる動画は正しい速度です。

VideoEncoderの出力はそのままでは動画ファイルにならない

WebCodecs の VideoEncoder が返すのは符号化されたフレームの列だけで、コンテナ(mp4やwebmの器)にはなりません。ブラウザにマルチプレクサは入っていないので、自分で組むか、ライブラリを足すことになります。

映像トラック1本だけなら、WebM(Matroska)の最小限のマルチプレクサは自前で書けます。 必要なのはEBMLのヘッダ・1つのトラック定義・キーフレームごとに区切ったクラスタだけです。音声を入れないと決めれば、実装はかなり小さくなります。

  • VP8 / VP9 は幅と高さが偶数である必要があります。 canvasのサイズを偶数へ丸めてから始めてください(奇数のままだと設定の時点で弾かれます)。
  • 対応コーデックは環境によって違います。 VideoEncoder.isConfigSupported() でVP9 → VP8の順に試して、通ったものを使うのが確実です。
  • ビットレートは「幅 × 高さ × fps × 係数」で見当を付ければ十分です。

静止画は「大きく描き直してから」撮る

画面のスクリーンショットは表示解像度そのままなので、壁紙やイラストの素材には小さすぎます。レンダラのサイズを一時的に上げてから1枚描き、撮ってから元に戻すのが正解です。

  • 先に画素密度を1へ固定してからサイズを指定する。 画素密度が掛かったままだと、指定した数字と実際のピクセル数が食い違います。
  • 上限を決めておく。 このサイトは4096pxで頭打ちにしています。GPU無しの環境では、これ以上は確保に失敗しえます。
  • finally で必ず画面用の設定へ戻す。 途中で失敗したときに戻し忘れると、プレビューが壊れたまま残ります(倍率・輪郭線・背景の3つとも)。

背景を透過させるには、レンダラを作るときに alpha: true を渡しておき、書き出しのときだけ背景をクリアします。検証は四隅と中央のアルファ値を読むこと — 見た目では透過しているかどうか分かりません(市松模様の背景に見えるのはビューアの機能です)。2倍・透過で書き出したとき、不透明な画素は全体の6.5%で、不透明な領域の範囲は不透過版と一致することを確認しました。

キャプチャが真っ黒になる原因

WebGLRenderer に preserveDrawingBuffer: true を渡していないと、canvas.toDataURL() が真っ黒な画像を返すことがあります。 WebGLは描画後にバッファをクリアしてよい仕様なので、レンダリングループの外(ボタンを押した瞬間)に読み出すと、既に消えていることがあるのです。

もう1つの原因はテクスチャの読み込み待ちです。モデルのローダのコールバックは「メッシュと材質が揃った」時点で呼ばれるだけで、テクスチャの取得はその後も非同期で続きます。 固定の待ち時間(1.5秒など)でキャプチャすると、テクスチャ22件のモデルで髪が破綻したサムネイルが保存されました(実測では取得完了まで7秒かかっていました)。未完了の件数を数えて、0になるまで待ってください。

保存する前に「絵が出ているか」を画素で確かめること。 背景色から離れた画素が全体の1%未満なら、それは失敗しているとみなして保存しない、という判定が効きます。真っ黒な画像が保存されてしまうと、後から見て原因が分かりません。

書き出しが正しいかを目で確かめない

この手の機能は「なんとなく出た」で終わらせると、破綻に気づけません。画素を数えて判定するのが確実です。

  • 絵が出ているか → 背景色から離れた画素の割合(実測 8.7%)。
  • 動いているか → 数秒あけて2枚撮り、画素を8×8に畳んだ指紋の差を見る(実測 2.4〜2.5%)。モデルが載っただけの静止画では差が0になるので、これで区別できます。
  • 色が変わったか → 平均色の1成分だけを見ないこと。ある材質を純赤にすると「Rはそのまま、GとBが下がる」ので、Rの増加を見ていると「効いていない」と読み違えます。赤み(R ÷ GBの平均)で測るのが正しい(実測 0.902 → 1.000)。

再生中は画面を消させない

動画を書き出す前に、そもそも最後まで再生できるかという問題があります。4分のモーションは無操作なので、スマホでは途中で画面が消えます。Screen Wake Lock を取れば防げますが、タブを裏に回すとOSに解除されるので、表に戻ったときに取り直す必要があります。