Y
UKAPERO.COM
個人開発者の現実

サーバー代ほぼ0円でオンライン動画編集サービスを作ってみた話(Next.js + Remotion)

1. はじめに

個人開発で動画系のWebサービスを作ろうとすると、真っ先に突き当たるのが 「サーバー代が高すぎて維持できない」 という問題です。レンダリングやエンコードをサーバー側で行うと、GPUサーバーの費用などで一瞬で財布が空になります。

この課題を回避するため、動画の編集からエンコード(書き出し)までを 「100%ユーザーのブラウザ(クライアントサイド)で行う」 という設計を採用し、サーバー代ほぼ0円で動作するチャット動画制作サービス 「ChatStoryStudio」 を個人で開発・公開しました。

  • サービスURL: ChatStoryStudio

  • 特徴: ログイン不要、インストール不要、基本無料でブラウザから即座に動画書き出しまで完結

全体デモ動画

この記事では、Reactで動画を作るライブラリ「Remotion」をブラウザ上で動かす方法と、開発中に遭遇した「日本語の文字化けや表示崩れバグ」をパッチで強行突破した泥臭い話、そしてサービスを破綻させないためのコスト設計について共有します。


2. オンライン動画生成サービスの「サーバーコスト破産」問題

通常、オンラインの動画編集サービスを作ろうとすると、以下のような構成が一般的です。

ステップ処理内容
1. 編集フロントエンドでユーザーが動画を編集する
2. 送信編集データ(JSONなど)をバックエンドに送信する
3. レンダリングバックエンドでFFmpegやヘッドレスブラウザ(Puppeteer)を起動し、サーバー側で動画をレンダリングする
4. 保存・配信レンダリング完了後、S3などのストレージに保存し、ユーザーにダウンロードさせる

この構成は手堅く動きますが、個人開発の規模では以下のデメリットが重すぎます。

デメリット項目詳細・発生する課題
高額なインフラ費用1本の動画を出力するのに数十秒〜数分間、サーバーのCPU/GPUを占有します。同時書き出しリクエストが増えればサーバーのスケール必要になり、月額費用が跳ね上がります。
ユーザーの順番待ちコストを抑えるためにサーバー数を制限すると、書き出しの「順番待ちキュー」が発生し、ユーザー体験が著しく低下します。
「使われるほど赤字になる」罠無料ユーザーが動画を書き出すたびにインフラ代を開発者が肩代わりすることになり、バズった瞬間にサーバー代で死にます。

そこで、 「動画のエンコード処理自体を、ユーザー自身のPCリソース(ブラウザ)に丸投げする」 というアプローチを取りました。


3. 解決策:すべてをブラウザに委ねる「クライアントサイド・レンダリング」

すべてをクライアントサイドで処理することにより、システム構成は以下のように極限までシンプルになります。

項目アプローチとメリット
ホスティング静的ファイル配信(Cloudflare Pages / Vercelなど)のみ。サーバー代はほぼ0円。
データ保存ユーザーが編集したタイムラインや素材データは、サーバーではなくブラウザのローカル(IndexedDB等)に保存。情報漏洩リスクがなく、ストレージ費用も0円。
レンダリングユーザーのブラウザがCanvas等の技術を利用して、その場で動画ファイルをエンコードしてローカルへ直接ダウンロード。

これにより、どれだけユーザーが増えて動画が書き出されても、開発者側のサーバー代はほぼ0円のままスケールさせることが可能になります。

「ローカルアプリ」と「従来のWebサービス」のいいとこ取り

ユーザーのPCリソースを使って動画を書き出すこと自体は、昔からあるローカルの動画編集ソフト(Premiere ProやAviUtlなど)と同じです。

しかし、本サービスがアプローチとして新しいのは、 「インストール不要のWebサービスでありながら、ローカル書き出しの仕組みを採用した」 という点です。

  • 従来のローカルアプリ: パソコンへのインストールやOSごとの動作制限があり、導入のハードルが高い。

  • 従来のWebサービス: インストール不要で手軽だが、動画の書き出しはサーバーで行われるため、開発者のインフラ負荷が大きく、無料での利用制限や順番待ちが発生しやすい。

近年のブラウザ機能の向上により、「アクセスした瞬間に使えて(Webの手軽さ)、処理はユーザーのPCで行う(ローカルの低コスト・安全性)」というハイブリッドな設計が、特別なソフトウェアのインストールなしで実現できるようになりました。


4. 技術の核心:Remotion + @remotion/web-renderer

この「ブラウザ完結の動画レンダリング」を実現するための核となるライブラリが Remotion です。

Remotionとは?

Remotionは、Reactコンポーネントを使って動画(MP4/WebMなど)を作成できるライブラリです。HTML5 CanvasやCSSアニメーション、Web Audio APIを組み合わせて動画のタイムラインを記述し、通常はCLIを介してサーバーサイドでエンコードを行います。

よくある「Remotionで動画作ってみた」系の記事との違い

ZennやQiitaでRemotionの事例を検索すると、その多くが「CLIでの書き出し」か「AWS Lambda等のサーバーサイドでレンダリングする構成」です。

しかし、本サービスでは、まだ日本語の技術ブログ等でもほとんど実用例が紹介されていない @remotion/web-renderer というパッケージを採用し、サーバーサイド処理を一切挟まない完全なフロントエンド(ブラウザ)完結のレンダリングを実現しています。

ブラウザでの書き出しを実現する @remotion/web-renderer

Remotionは長い間サーバーサイドでのレンダリングが基本でしたが、現在はブラウザ上だけでレンダリングを行うパッケージ @remotion/web-renderer が(Experimentalな機能として)提供されています。

これを使うことで、サーバーを介さずにブラウザ内で動画の各フレームをHTML5 Canvasに描画し、WebM等のフォーマットにエンコードしてユーザーへ直接出力することができます。

フロントエンド(Next.js)での実装イメージ

import { renderMediaOnWeb } from "@remotion/web-renderer";

async function exportVideo() {
  const baseRenderOptions = {
    composition: {
      id: "ChatVideoComposition",
      // 描画用のReactコンポーネントを指定
      component: MyVideoComponent,
      durationInFrames: 150, // 例: 5秒の動画 (30fps)
      fps: 30,
      width: 1080,
      height: 1920,
    },
    // ユーザーが編集したタイムライン等のデータをPropsとして渡す
    inputProps: {
      timelineData: currentTimeline, // タイムラインなどのデータ
    },
    container: "mp4",
    videoCodec: "h264",
    onProgress: ({ encodedFrames }) => {
      console.log(`進行状況: ${encodedFrames} フレーム完了`);
    }
  };

  let result;
  try {
    // 1. まずはハードウェアアクセラレーション優先で高速レンダリングを試行
    result = await renderMediaOnWeb({
      ...baseRenderOptions,
      hardwareAcceleration: 'prefer-hardware',
    });
  } catch (err) {
    // 2. ブラウザやマシンの相性で失敗した場合は、ソフトウェアエンコーディングにフォールバック
    console.warn("ハードウェアエンコード失敗。ソフトウェアで再試行します", err);
    result = await renderMediaOnWeb({
      ...baseRenderOptions,
      hardwareAcceleration: 'prefer-software',
    });
  }

  // レンダリング完了後、Blobオブジェクトを取得(非同期メソッド)
  const videoBlob = await result.getBlob();
  const url = URL.createObjectURL(videoBlob);
  
  // ダウンロード処理の実行
  const a = document.createElement("a");
  a.href = url;
  a.download = "chat_story.mp4";
  a.click();
}

この処理はすべてユーザーのブラウザプロセスで実行されるため、サーバー側で重いFFmpegプロセスを走らせる必要が一切ありません。また、ハードウェアエンコードの失敗に対して自動でソフトウェアエンコードへフォールバックさせる処理を入れることで、ユーザーの環境依存によるエラー落ちを防ぐ設計になっています。


5. 日本語環境で動かすための罠とパッチ対応

@remotion/web-renderer によるブラウザレンダリングは非常に強力ですが、英語圏のライブラリであるため、日本語環境ではいくつかの問題にぶち当たりました。

罠その1:日本語テキストの文字化け・重なり表示バグ

Remotion内部でテキストを自動折り返し(line break)させるための単語切り出しロジックにおいて、以下のように英語(en)の単語単位(word)でのセグメンテーションがハードコードされています。

// Remotion内部のコード(find-line-breaks)
const segmenter = new Intl.Segmenter("en", { granularity: "word" });

英語であれば「単語の区切り(スペース等)」で改行位置を計算すれば問題ありません。しかし、日本語は単語がスペースで区切られていないため、英語用のセグメンターを通すと文字の区切りやテキスト幅の計算が狂ってしまいます。結果として、改行はされるものの一部の文字が化けたり、複数行のテキストがなぜか同じ座標に重なって表示されるという不自然極まりない描画崩れが発生しました。

これを回避するため、patch-package を用いて、node_modules 内の該当ロジックを以下のように書き換えるパッチを適用しました。

diff --git a/node_modules/@remotion/web-renderer/dist/esm/index.mjs b/node_modules/@remotion/web-renderer/dist/esm/index.mjs
index a72b622..b663cc1 100644
--- a/node_modules/@remotion/web-renderer/dist/esm/index.mjs
+++ b/node_modules/@remotion/web-renderer/dist/esm/index.mjs
@@ -3283,7 +3283,7 @@ var applyTextTransform = (text, transform) => {
 // src/drawing/text/find-line-breaks.text.ts
 var findWords = (span) => {
   const originalText = span.textContent;
-  const segmenter = new Intl.Segmenter("en", { granularity: "word" });
+  const segmenter = new Intl.Segmenter("ja-JP", { granularity: "grapheme" });
   const segments = segmenter.segment(span.textContent);
   const words = Array.from(segments).map((s) => s.segment);
   const tokens = [];

ja-JPgranularity: "grapheme"(書記素単位)に指定することで、単語単位ではなく「文字(グリフ)単位」でセグメント化されるようになり、日本語の吹き出し内でも綺麗に自動改行が行われるようになります。

NOTE

このような日本語対応が必要になっているのは、@remotion/web-renderer が現時点でまだ Experimental(実験的機能) のステータスだからです。今後正式リリースされる際には、他言語の折り返しルールや Intl.Segmenter のカスタムオプションが本家側で考慮・整備され、パッチなしで綺麗に動くよう改善される可能性が高いと考えられます。

罠その2:日本語データ(Props)が1文字でも入るとbtoaで即クラッシュする問題

@remotion/web-renderer で動画生成を開始する際、会話データ(inputProps)に日本語のマルチバイト文字が1文字でも含まれていると、ブラウザ標準の btoa 関数の仕様制限(Latin1範囲外の文字エラー)により、エンコード例外を発生させてプロセスが即座にクラッシュします。

これを防ぐため、実際の ChatStoryStudio では、会話データを直接渡すのをやめ、Unicode(マルチバイト文字)に対応した独自のBase64シリアライズ処理を噛ませて受け渡しを行っています。

  • 送信側 (Webクライアント): encodePropsToBase64 でマルチバイト文字対応のBase64に変換して inputProps に設定。

  • 受信側 (Remotionコンポーネント): decodePropsFromBase64 でデコードしてデータを復元して描画。


6. APIコストとビジネス設計のバランス

「サーバーコストがほぼ0円」とはいえ、本物のサービス(SaaS)として本番運用するにあたり、完全にコストが0なわけではありません。実際には以下のコストが発生しています。

  1. Webサーバー・APIホスティング費用(月数百円〜):
    APIやバックエンドの処理に Cloudflare Workers の有料プランを採用しています。リクエスト数に応じた従量課金ですが、初期フェーズでは月額約5ドルの最小限のインフラ維持費で抑えられています。

  2. 音声合成(TTS)のAPI費用:
    キャラクターに感情豊かな声を設定するため、外部の高品質TTS APIをコールしています。

  3. AI脚本読み込み(台本自動生成)のAPI費用:
    テキスト台本からタイムラインを自動でパース・生成するためのAPI費用。

コストを破綻させないためのサービス設計

これら外部APIを叩くコストに対し、本サービスでは以下のようなビジネス・システム設計でバランスを取っています。

  • 基本機能は完全無料化:
    「チャット動画を自分で組み立てて、ブラウザで書き出す」というコア機能については、外部APIを一切消費しない(サーバーコストが全く発生しない)ため、制限なく無料で開放しています。

  • 無料枠の制限と低単価モデルの採用によるコストコントロール:
    音声合成(TTS)やAI脚本生成などの付加価値機能は、無料(Free)プランのユーザーにも一定回数の「無料お試し枠」を提供しています。これが可能なのは、 「API単価が極めて安いモデル(Gemini TTSや、コストパフォーマンスに優れた軽量なLLMモデル)」 を厳選して採用しているためです。
    個人開発者が許容できるレベルの回数制限(クォータ)をかけることで、仮に無料ユーザーが急増したとしても、開発者の負担額が破綻しない安全な上限に収まるように設計しています。

  • ヘビーユースは有料サブスク(Proプラン)で回収:
    お試し枠を超えてこれらの機能を日常的に使い倒したいユーザーに対しては、有料サブスクリプション(Stripe連携)への移行を要求し、APIの利用コストを相殺・黒字化する仕組みにしています。

  • ローカル書き出しの手軽さによるインフラ比較:
    バックエンド側で重い動画サーバーを動かし続けた場合の「月数万円〜の固定インフラ費」に比べれば、従量課金されるTTSやLLMのAPI費用ははるかに軽微であり、サブスク料金内で余裕を持って黒字化・カバーできる計算になります。


7. クライアントサイド動画生成のデメリットとトレードオフ

サーバー費用を極限まで抑えられる一方で、高負荷な処理をユーザーのブラウザに委ねることには、以下の明確なデメリットと限界が存在します。

  1. ユーザー端末への処理負荷とスペック要求:
    エンコード中はユーザーのCPU/GPUリソースをフルに消費するため、一時的にPCの動作が重くなったり、ファンの回転やバッテリーの激しい消費が発生します。また、低スペックなPCでは書き出しに著しく時間がかかります。

  2. ブラウザのメモリ制限によるクラッシュ:
    特にスマートフォン(iOS等)やメモリが不足している環境では、長尺の動画や重いアセットを含む動画を書き出そうとした際、ブラウザのメモリ上限(制限)に達してタブがクラッシュすることがあります。本サービスで「PC環境を強く推奨」としているのもこれが理由です。

  3. 環境依存によるデバッグの困難さ:
    サーバーサイド処理であればエラーログを一元管理して原因を追究できますが、クライアントのブラウザ環境(インストールされている拡張機能の干渉、ハードウェアとブラウザの相性など)で発生する個別エラーは、開発者側からの特定・デバッグが非常に困難です。


8. まとめ

これまで「動画編集・生成サービス」は個人開発者にとって、インフラの維持費と実装の複雑さから手が出しづらい領域でした。

しかし、 Next.js + Remotion(web-renderer) を組み合わせることで、これらのトレードオフを許容できるユースケースであれば、 「どれだけ使われてもサーバー代がほぼ増えない動画編集サービス」 を個人で立ち上げることが十分に可能になります。

現時点では、まだ実験的機能ゆえに日本語対応(Intl.Segmenter のパッチ適用やエンコード対策)など泥臭い回避策が必要になりますが、クライアントサイドへの処理委託(ローカルファースト)は、個人開発の可能性を大きく広げる強力なアプローチです。

サーバー代の赤字に怯えながら個人開発をするのは精神衛生上よろしくないので、動画系のプロダクトを作ろうとしている方は、クライアントサイドレンダリングを逃げ道として検討してみると幸せになれるかもしれません。

本記事がお役に立ちましたら、ぜひシェアいただけると嬉しいです!

avatar
Yukapero
大手IT企業を血迷って退職し、独りでWebサービス/アプリ開発に没頭するクレイジーなエンジニア。個人開発やフリーランスエンジニアとしての活動の過程での成功と失敗、得られた知見や日々の苦悩まで幅広く発信しています。