Unity WebGLの読み込み時間とfpsを測る方法(軽量化の前後で比較)

ダウンロード 0.76秒、起動 0.6秒、プレイ中 60.1fps。10.1MB 版と 7.37MB 版を同じ条件で比べたら起動が19%速くなりました。計測ページの作り方と、つまずいた点を残します。

Libra

公開 2026年9月16日 ・ 更新 2026年9月18日

この記事の要点7項目

ダウンロードは 7,325,986 バイトで 0.755秒(約78Mbps の回線)。 そこから遊べるようになるまで 0.62秒。

残り6つの要点を読む
  • 軽量化の前後を同じ条件で比べると、起動が 約705ms → 約569ms(−19%)になった。
  • 圧縮を外したあとのサイズは wasm が 38.3MB → 23.7MB(−38%)。 ダウンロード量(−23%)より大きく減っている。
  • スマホ幅では devicePixelRatio 2 のせいでキャンバスの実解像度が 750×1624 になる。 画素数は2倍かかる。
  • プレイが続いている状態の fps は 60.1。 60Hz のディスプレイなので上限に張り付いている。
  • 垂直同期を切って測ると 1フレーム 2.03ミリ秒。 60Hz の 16.67ミリ秒に対して 14.64ミリ秒の余裕があった。
  • fps は非表示のタブでは測れない。 ただし自動操作で本物のウィンドウを出せば自動で測れる(2026-09-18 追記)。
目次9章
検証した環境最終確認 2026年9月18日
  • Unity 6.6(6000.6.0f1)・WebGL・Brotli 圧縮
  • Windows 11・Chromium 系ブラウザ
  • 配信は VPS 上の Caddy(Content-Encoding: br)
  • 回線:光(実測 約78Mbps でこのサーバーから取得)

書いてあるのは、この環境で実際に試した結果です。ツールの仕様や料金は変わるので、重要な判断の前には公式の情報も確認してください。

ビルドを 10.1MB から 7.37MB に減らしました。で、実際どれだけ速くなったのか。

サイズの記事に「測れたら追記する」と書いたので、測りました。

結果

0.76秒

ダウンロード

7,325,986 バイト・約78Mbps

0.6秒

遊べるようになるまで

ダウンロード済みから

約1.4秒

初めて開いたとき

上の2つの合計

60.1

プレイ中の fps

60Hz のディスプレイ

ダウンロード済みから遊べるまで(同条件・中央値)
  • 10.1MB 版約705 ms
  • 7.37MB 版約569 ms

ローカルサーバーで4〜6回ずつ測った中央値。回線の影響を外した、展開とコンパイルの時間です。

起動が19%速くなりました。 ダウンロードが27%減った分に加えて、展開とコンパイルの時間も減っています。

ダウンロードの内訳(本番・光回線)

配信は自分の VPS(Caddy が Content-Encoding: br を返す)です。ブラウザのキャッシュを使わずに取り直して測りました。

ファイルごとのダウンロード時間
  • wasm.br(5,635,795 バイト)545 ms
  • data.br(1,618,640 バイト)190 ms
  • framework.js.br(71,551 バイト)20 ms

合計 7,325,986 バイトを 755 ms。ほぼ wasm の時間です。

約78Mbps 出ています。この回線なら 1.4秒で遊び始められる。 ただし、これは速い回線での話です。実際には回線が細いほど、サイズを減らした効果が大きく出ます。

キャッシュの2段構え

Unity の WebGL は dataCaching が有効だと、展開したデータを IndexedDB(UnityCache)に置きます。ブラウザの HTTP キャッシュとは別なので、計測するときは両方消す必要があります。消さずに測ると、2回目以降は転送が304になって速く見えます。

見落としやすい数字:圧縮を外したあとのサイズ

ダウンロード量ばかり見ていましたが、展開後のサイズのほうが大きく減っていました。

wasm のサイズ(10.1MB 版 → 7.37MB 版)
  • 10.1MB 版:展開後38,332,803
  • 7.37MB 版:展開後(−38.3%)23,663,474
  • 10.1MB 版:転送量7,333,280
  • 7.37MB 版:転送量(−23.1%)5,635,795

ダウンロード量より、展開後のほうが大きく減っている。

10.1MB 版7.37MB 版差
wasm 転送量7,333,2805,635,795−23.1%
wasm 展開後38,332,80323,663,474−38.3%
data 転送量2,689,4771,618,640−39.8%
data 展開後10,373,6886,706,246−35.4%

展開後のサイズはメモリと wasm のコンパイル時間に効きます。14.7MB 分のコードをコンパイルしなくてよくなったということです。起動が19%速くなったのは、ここが効いています。

この数字はブラウザの Resource Timing から取れます。

performance.getEntriesByType('resource').map(r => ({
  file: r.name.split('/').pop(),
  transferred: r.encodedBodySize,   // 転送量(圧縮後)
  expanded:    r.decodedBodySize,   // 展開後
}))

計測用のページを作る

本番のページにはローディング表示や全画面ボタンが載っていて、どこが「遊べるようになった瞬間」なのかが分かりにくい。計測用の小さい HTML をビルドのフォルダに置きました。

<canvas id="c" width="800" height="450"></canvas>
<script>
var t0 = performance.now();
var s = document.createElement("script");
s.src = "Build/WebGL.loader.js";
s.onload = function () {
  createUnityInstance(document.getElementById("c"), {
    dataUrl:      "Build/WebGL.data.br",
    frameworkUrl: "Build/WebGL.framework.js.br",
    codeUrl:      "Build/WebGL.wasm.br",
    streamingAssetsUrl: "StreamingAssets",
    companyName: "m", productName: "m", productVersion: "1"
  }).then(function () {
    window.READY = performance.now() - t0;   // ここが「遊べるようになった」時刻
  });
};
document.body.appendChild(s);
</script>

これをビルドごとに置けば、同じ物差しで比べられます。

⚠️ 同じタブで測り直すと2回目から止まる

最初は同じタブで何度も読み込み直していました。4回目あたりから進捗0のまま固まります。 WebGL のコンテキストには上限があり、前のインスタンスを終了せずに新しく作れないからです。エラーも出ません。計測ごとにタブを開き直してください。

スマホ幅で見る(エミュレーション)

実機ではなく、ブラウザの端末エミュレーション(Pixel 8 の UA・375×812・タッチ5点)です。

  • 起動する。コンソールにエラーなし
  • 縦画面いっぱいにレイアウトされ、HUD とブロックとパドルが全部入る
  • タップで開始・発射でき、スコアも入る

ただし1つ気になる数字がありました。

375×812

CSS 上のサイズ

Pixel 8 のエミュレーション

×2

devicePixelRatio

高精細の画面

750×1624

キャンバスの実解像度

約122万画素を描く

描画している画素は 121.8万。スマホの GPU にとっては軽くない量です。Unity の WebGL テンプレートには config.devicePixelRatio = 1; という行(既定ではコメントアウト)があり、これを有効にすると等倍で描いて負荷を落とせます。見た目は少しぼやけます。

実機で fps を測ってから決めることにしました。

fps:60.1(ただし自動では測れなかった)

プレイ中の実測です。

60.1

fps

ボールが動いている状態・10秒平均

603ms

起動まで

同じ計測ページで

60Hz

ディスプレイ

1920×1080・これが上限

環境は Chrome(Windows 11)・GeForce RTX 2080 Ti です。

60 に張り付いています。 ただしこれは「速い」ことの証明にはなりません。ディスプレイが 60Hz なので、上限に当たっているだけです。1フレームにどれだけ余裕が残っているかは、この測り方では出ません。低い性能の端末で測るのが次の手です。

詰まったところ:非表示のタブでは測れない

requestAnimationFrame の呼ばれた回数を10秒数えれば fps が出る。そう思って自動操作のブラウザで走らせたら、10秒で1回しか呼ばれませんでした。

計測スクリプトの結果(自動操作のブラウザ)エラー
{ n: 1, fps: null, hidden: true }

document.hidden が true。画面に映っていないタブでは requestAnimationFrame が進みません。 Unity の描画ループも同じ仕組みなので、そもそも描画されていません。バックグラウンドのタブに切り替えても同じです。

同じ理由で、ページを開いた直後の1回目は低く出ます。 ウィンドウを切り替えている最中のフレームが欠けるためです。10秒ごとに測り直して、2回目以降の値を見てください。

結局このときは、fps だけ人が見ている画面で測りました。 計測用ページに数字を出して、実際に遊んでもらった値が上の 60.1 です。

追記:自動でも測れました(2026-09-18)

「見えていないと測れない」は正しいのですが、自動操作でも、本物のウィンドウを出せば見えている状態になります。 ヘッドレスではなく画面に出るモードで Chrome を起動し、そのページで requestAnimationFrame を数えたら、そのまま測れました。

自動計測の結果(スマホ幅・端末の倍率2)
readyMs        1935   (ダウンロードを含めて遊べるまで)
downloadMs     1173
transferred    7,294,651 バイト
fps            60.0
worstFrameMs   17.0
canvas         375 x 812(30.5万画素)

⚠️ ヘッドレスでは意味が変わる

画面を出さないモード(ヘッドレス)でも動きますが、多くの環境でGPU ではなくソフトウェア描画になります。数字は出ても「その端末の実力」ではなくなるので、fps の計測には使いません。

同じ仕組みで、スマホ幅・端末の倍率2の条件も再現できます。描画解像度を下げる設定(config.devicePixelRatio = 1)の効果を測ったところ、実解像度は 750×1624(121.8万画素)から 375×812(30.5万画素)へ1/4になりましたが、この PC ではどちらも 60.0fps でした。60Hz の上限に当たっているので、差が出るのは非力な端末です。

実機でしか分からない

実機の fps・発熱・電池の減り・操作感

自動で回せる

サイズ・ダウンロード時間・展開後のサイズ・起動までの時間・PC での fps と実解像度

60fps の「余裕」は、垂直同期を切って測る(2026-09-18 追記)

ブラウザの 60fps は、画面の更新間隔に張り付いているだけです。余裕がどれだけあるかは別に測る必要があります。エディターの Play モードで垂直同期を切って、1フレームの所要時間を集めました。

2.03ms

1フレームの所要時間

中央値・492fps 相当

2.74ms

p95

遅い方から5%

14.64ms

60Hz での余裕

16.67ms - 2.03ms

最長のフレームは 12.13ミリ秒でした。つまり、いちばん重いフレームでも上限には届いていません。ブロック崩し程度なら、この PC では8倍くらいの余裕があるということになります。

⚠️ ここでも「見えていないと測れない」問題が出た

最初の計測は、全サンプルが 439.42ミリ秒(2.28fps) で完全に同じ値になりました。Unity エディターは前面に無いとフレームを進めないためです。Application.runInBackground を true にしたら、正しい値が取れました。非表示のタブで requestAnimationFrame が止まるのと同じ性質です。

計測に使ったのは50行ほどのエディター用スクリプトです。EditorApplication.update で Time.unscaledDeltaTime を集め、最初の30フレームを捨てて中央値と p95 を出しています。

まとめ

  • ダウンロード 0.76秒+起動 0.62秒=約1.4秒(78Mbps の回線で)
  • 軽量化で起動が19%速くなった(705ms → 569ms)
  • 展開後のサイズはダウンロード量より大きく減る(wasm −38%)。ここが起動時間に効く
  • 計測はタブを毎回開き直す。同じタブだと WebGL のコンテキストが尽きて黙って止まる
  • fps は 60.1。ただし 60Hz の上限に当たっているだけで、余裕の量までは分からない
  • fps は見えているタブでしか測れない。ただし自動操作で本物のウィンドウを出せば測れる(2026-09-18 追記)
  • 余裕の量は垂直同期を切ったエディターで測れる。1フレーム 2.03ミリ秒=60Hz に対して 14.64ミリ秒の余裕

よくある質問

よくある質問

読み込み時間はどこからどこまでを測ればいいですか?

ページを開いた瞬間から、操作できるようになるまでです。今回は createUnityInstance が解決した時点にしました。Unity のテンプレートは進捗が1になった時点でローディング表示を消すので、そこを見ても近い値になります。

同じタブで測り直すと値が変になります。

WebGL のコンテキストには数の上限があり、前のインスタンスを終了せずに読み込み直すと新しいインスタンスが作られません。進捗0のまま止まります。計測ごとにタブを開き直すのが確実です。

展開後のサイズは何に効きますか?

メモリと、wasm のコンパイル時間です。ダウンロードが同じでも展開後が小さいほど起動は速くなります。今回はダウンロードが23%減に対して、展開後の wasm は38%減っていました。

スマホでの実測はしましたか?

していません。ブラウザの端末エミュレーション(Pixel 8 の UA・375×812・タッチ5点)で動作とレイアウトは確認しましたが、これは PC の性能で動いています。実機の fps と読み込み時間は別に測る必要があります。

fps はどう測ればいいですか?

requestAnimationFrame の呼ばれた回数を数えるのが簡単です。ただし非表示のタブでは呼ばれません。自動操作のブラウザで測ろうとしたところ、10秒間で1回しか呼ばれませんでした(document.hidden が true)。画面に見えている状態で測ってください。

60.1fps は「余裕がある」という意味ですか?

そこまでは分かりません。ディスプレイが 60Hz なので、上限に当たっているだけです。1フレームにどれだけ余裕があるかは、この測り方では出ません。低い性能の端末で測るか、垂直同期を外して測る必要があります。

この記事は役に立ちましたか?

関連記事