マガジン

「遅い端末でも」と付けると、何が軽くなるか

遊びをつくる

執筆にClaudeを使用しています。

ボタン、歯車、ベルが順につながり、操作・状態・反応を示す紙細工風イラスト。
記事のテーマをもとに制作したAI生成イラスト。

AIにブラウザゲームを頼むとき、依頼文の最後に「遅い端末でも動くようにしてください」と添える場面を考えてみます。この一言は、生成されるコードのどこに現れるのでしょうか。描画の回数が減るのか、計算の方式が変わるのか、それとも見た目は変わらず、コメントに「軽量化済み」と書かれるだけなのか。答えは使うAIや仕様によって変わるため、ここでは結果を示しません。 代わりに、自分の環境で答えを出すための実験を設計します。同じ仕様で、最後の一行だけが違う依頼を複数回出し、生成されたコードを観点ごとに読み比べます。さらに、動作を意図的に遅くした状態でフレーム間隔を測ります。コードの読み比べと実測の両方を使うのは、片方だけでは「それらしい違い」と「本当に軽くなった違い」を区別できないからです。

この記事の目次
  1. 条件を三つ用意する
  2. 手順
  3. 記録の取り方
  4. 結果の読み方と起こりやすい誤り
  5. 次の行動

条件を三つ用意する

比べる条件は三つです。仕様の文章は一字も変えず、末尾の一行だけを差し替えます。

共通の仕様の例です。

落ちてくる星を、左右に動くカゴで受け止めるブラウザゲームを、単一のindex.htmlで作ってください。星は時間とともに増えます。受け止めると光の粒が散ります。外部ライブラリと外部通信は使わないでください。

この仕様は、描画(星・粒・背景)と計算(落下・衝突)の両方が時間とともに重くなる作りです。性能への一言が効く余地があるので、題材に向いています。別のゲームでも構いませんが、数が増えていく要素を入れておくと差が見えやすくなります。

  • 条件A(一言なし):仕様のみ。
  • 条件B(漠然とした一言):仕様の末尾に「遅い端末でも動くようにしてください。」を足します。
  • 条件C(具体的な一言):仕様の末尾に「描画する数と判定の回数を抑え、画面が変わらないときの無駄な処理を避けてください。」を足します。

条件Cは、Bとの差を見るための比較対象です。漠然とした言葉と具体的な言葉でどれだけ差が出るかが分かると、次に自分の依頼文へ何を書くかを決めやすくなります。

準備するものは次のとおりです。

  • 同じAIの同じモデル。途中で切り替えません。
  • 条件ごとに5回ずつ、合計15回の生成。負担が大きければ3回ずつでも構いません。ただし回数が少ないほど、後述の判断を弱めに読む必要があります。
  • 開発者ツールでCPUの処理を遅くできるブラウザ。Chromeの開発者ツールには、CPUの速度を倍率で落とす設定があります。
  • 測定用の小さなスクリプト(後述)。
  • 記録用の表計算シートか紙。

手順

  1. 生成のたびに、新しい会話を開きます。同じ会話で続けると、前の回の出力が次の回に影響し、条件の差が混ざります。
  2. 15回の出力に、A1〜A5、B1〜B5、C1〜C5と名前を付け、A1.html のように保存します。どれがどの条件かは、読み比べの段階では意識しすぎないようにします。可能なら、ファイル名を乱数にして対応表を別に持つと、読み取りの偏りを減らせます。
  3. 各ファイルを、次の節のチェックリストでコードから読みます。実行する前に読み終えます。実行後だと、動きの印象が読み取りに影響するからです。
  4. 各ファイルに測定スクリプトを貼り、CPUを遅くした状態で30秒、同じ操作で遊びます。操作は「カゴを左右に往復させ続ける」のように単純にそろえます。
  5. 結果を表にまとめ、条件ごとに並べます。

記録の取り方

コードから読む観点

コードを読むときは、次の観点について「はい・いいえ・該当なし」で記録します。数えられるものは数も書きます。

  • ループの種類、見る場所:requestAnimationFrame か setInterval か、軽くする方向の例:画面更新に合わせて呼ばれる前者が基本です
  • 時間の扱い、見る場所:前回との経過時間を使うか、1フレーム固定量か、軽くする方向の例:経過時間を使えば、遅い端末でも進行速度がそろいます
  • 描画の重さ、見る場所:shadowBlur、グラデーション、filter の使用数、軽くする方向の例:使う数や面積を減らします
  • 毎フレームの生成、見る場所:filter、map、スプレッドで新しい配列を作っていないか、軽くする方向の例:上限付きの配列を使い回します
  • 判定の方式、見る場所:星と粒の全組み合わせを毎回調べていないか、軽くする方向の例:判定対象を絞ります
  • 数の上限、見る場所:星や粒の最大数が決まっているか、軽くする方向の例:上限を設けます
  • 解像度、見る場所:devicePixelRatio を掛けているか、キャンバスの大きさ、軽くする方向の例:掛けすぎない、あるいは上限を付けます
  • 非表示時、見る場所:visibilitychange の扱い、軽くする方向の例:見えていないときは止めます

「該当なし」を作っておくのは、AIが別の方式を選んだときに判断が崩れないようにするためです。たとえばDOMで星を動かす実装が出たら、キャンバスの観点は該当なしとし、DOMの要素数を別に記録します。

実測する観点

ゲームの実行時間を測るには、開発者ツールのパフォーマンス記録が詳しいのですが、15本分を毎回読み込むのは手間がかかります。そこで、フレームとフレームの間隔を記録する簡単なスクリプトを使います。画面更新に間に合わないほど処理が重いと、この間隔が伸びます。

次のコードを、各ファイルの </body> の直前に貼ります。

html
<script>
(() => {
  const gaps = [];
  const START = performance.now();
  const DURATION = 30000;
  let last = START;
  function tick(now) {
    gaps.push(now - last);
    last = now;
    if (now - START < DURATION) {
      requestAnimationFrame(tick);
    } else {
      report();
    }
  }
  function report() {
    const g = gaps.slice(30);          // 最初の30フレームは除く
    const s = [...g].sort((a, b) => a - b);
    const avg = g.reduce((a, b) => a + b, 0) / g.length;
    const p95 = s[Math.floor(s.length * 0.95)];
    const max = s[s.length - 1];
    const slow = g.filter(v => v > 33.4).length;
    console.log(
      `平均 ${avg.toFixed(1)}ms / 95% ${p95.toFixed(1)}ms / ` +
      `最大 ${max.toFixed(1)}ms / 33ms超 ${slow}回 / 全${g.length}フレーム`
    );
  }
  requestAnimationFrame(tick);
})();
</script>

30秒後に、開発者ツールのコンソールへ平均・95パーセンタイル(遅いほうから数えて上位5%の手前の値)・最大・33ms超の回数が出ます。平均だけでなく95%と最大を残すのは、遊んでいて不快なのは平均より「ときどき止まる」ことのほうだからです。

測り方で守ることがあります。

  • 測るファイルごとに、同じ倍率のCPU低速化を設定します。倍率は最初に1つ決め、全ファイルで変えません。低速化なしの状態でも、一度は全ファイルを測っておくと、余裕のある環境では差が出ないことを確認できます。
  • 同じ画面サイズ、同じブラウザのウィンドウで測ります。
  • 読み込み直後はフレームが乱れやすいので、前述のとおり最初の30フレームを除いています。
  • ディスプレイのリフレッシュレートも記録しておきます。60Hzなら間隔の下限はおよそ16.7ms、120Hzなら約8.3msです。機器が違うと基準が変わります。

結果の読み方と起こりやすい誤り

15本分の表ができたら、まず条件内のばらつきを見ます。たとえば条件Aの5本の「95%値」が最小から最大までどの範囲に広がるかを書き出します。次に条件BとCについても同じ範囲を書き、範囲の重なりを見ます。

  • 範囲が重なっている:一言の効果とは言い切れません。生成のたびの偏りの範囲に収まっています。回数を増やして確かめます。
  • 範囲が重ならず、チェックリストにも差がある:一言が効いた可能性が高いと読めます。どの観点が変わったかを見て、その観点が実測の差を説明できるかを確認します。
  • チェックリストには差があるのに、実測がほとんど変わらない:軽くしたつもりの変更が、実際の負担の中心ではなかったと読めます。パフォーマンス記録で、時間を使っている処理を確かめます。
  • 実測は軽くなったが、見た目や遊び心地が変わった:軽さの代償があります。光の粒が減っていたり、星の最大数が小さくなっていたりしないか、遊んで確かめます。軽さと見た目のどちらを優先するかは、自分のゲームで決めることです。
  • チェックリストに差がなく、実測にも差がない:言葉が反映されなかったか、コメントだけが変わったと読めます。

条件Bと条件Cの比べ方も重要です。Cだけが変わるなら、性能に関する依頼は具体的に書いたほうが伝わりやすいことになります。BとCがほぼ同じなら、短い一言でも足りることになります。どちらになるかは、自分で測るまで分かりません。

起こりやすい誤りは次のとおりです。

  • 1回の生成で判断する:同じ依頼でも、生成のたびに実装はばらつきます。条件ごとに複数回出し、範囲で比べます。
  • 同じ会話で続けて生成する:前の出力が文脈に残り、条件の差が混ざります。毎回新しい会話にします。
  • 低速化を付けたまま、別の作業をする:低速化は開発者ツールを開いている間だけ有効なことが多く、設定の状態を忘れやすい点にも注意します。測定の前後で設定を確認します。
  • 別のタブが裏で動いている:ブラウザの拡張機能や他のタブが処理を奪うと、結果が揺れます。測定用のウィンドウは1つにします。
  • 測定中にタブを切り替える:非表示のタブでは画面更新が止まり、間隔が極端に大きく記録されます。30秒間は表示したままにします。
  • 操作が毎回違う:カゴを動かす速さや受け止める数が違うと、画面の負荷も変わります。往復の速さをそろえます。
  • 「軽い」という自己申告を信じる:コード内のコメントや、AIの説明文にある「軽量化しました」は、証拠になりません。観点表と実測で確かめます。

次の行動

まず条件AとBだけで、3回ずつの6本を作ってみてください。末尾の一行が、コードのどの観点に現れるかを表に書くだけでも、自分が使うAIの癖が見えてきます。気に入った版ができたら、単一の index.html にまとめて、アソバースに投稿して確かめる方法もあります。ファイルは20MiB以内で、外部通信を使わない作りなら、そのまま試せます。ただし、アソバースのプレイ回数は作品の起動回数であり、端末ごとの性能を示す数字ではありません。軽さの確認は、この記事の測定で自分で行ってください。