🌿 中級⏱ 所要時間の目安:遊べるまで1〜2時間/PWA化まで半日〜1日使うツール:Claude Code / HTML5 Canvas / JavaScript / PWA

ブラウザゲーム・スマホで遊べるアプリをAIエージェントで作る【2026】HTML5 Canvas+PWA

「子どもと一緒にゲームを作ってみたい」「社内イベントで使えるミニゲームがほしい」——ゲームは、AIエージェントとの開発を体験するのにいちばん楽しい題材です。遊びのルールを言葉で伝えるだけで動くものが出てきて、遊んだ感想をそのまま修正依頼にできるからです。この記事では、Claude Codeと一緒にHTML5 Canvasのブロック崩しを作り、スマホのタッチ操作に対応させ、ホーム画面に追加してオフラインでも遊べるPWAにするまでを、やりとりの順番どおりに再現します。

🔬
調査・編集:AIエージェント研究所 編集部
運営=広告運用・生成AIエージェント導入支援の現役コンサル。このサイト自体も、AIエージェントと一緒に作り・公開・運用しています。

現場でAIエージェントと開発している進め方をベースに、再現できる手順とコードで解説します。→ 運営者・編集方針について

公開:2026年9月26日/最終更新:2026年9月26日
この記事の結論(要点まとめ)
  • 最初は「1ファイルで動くもの」を作らせ、すぐ遊ぶ。「難しすぎる」「音がほしい」など遊んだ感想がそのまま次の指示になる
  • ゲームの骨格はrequestAnimationFrameのゲームループ+当たり判定+状態管理。数値(速さ・大きさ)は定数にまとめさせると調整がラク
  • スマホ対応はPointer Events+touch-action: none、音はタップの後でしか鳴らせない。PWAはmanifest+service worker+HTTPSでホーム画面に追加&オフライン対応

完成イメージ:こんなゲームを作ります

作るのは縦長画面の「ぽこぽこブロック崩し」。画面下のパドル(板)を指やマウスで左右に動かし、ボールを跳ね返して上のブロックを全部壊すと、少し速くなって次の面へ進みます。

SCORE 120 LIFE 2 BEST 340
●

※画面イメージ(実際はCanvasに描画されます)

  • PCでもスマホでも遊べる(マウス/指で操作)
  • ブロックを壊すと効果音、ハイスコアを端末に保存
  • スマホのホーム画面にアイコンを追加でき、アプリのように全画面で起動。機内モードでも遊べる
  • サーバーもデータベースも不要。HTML・JavaScriptなどの数ファイルだけで完結

ゲームの心臓部は、1秒間に約60回「①入力を読む → ②位置を動かして当たりを調べる → ③描き直す」を繰り返すゲームループです。パラパラ漫画を高速でめくっているようなものだと考えてください。

① 入力Pointer Events② 更新update(dt)③ 描画draw()④ 次のコマへrequestAnimationFrameパドル位置新しい位置描き終えた画面更新に合わせて1秒に約60回くり返す指・マウス動かすCanvas(画面)全部描き直す
ゲームループは「①入力(指・マウスの位置でパドルを動かす)→②更新(ボールを進め、壁・パドル・ブロックとの当たりを判定)→③描画(Canvasを塗りつぶして全部描き直す)→④次のコマへ」を、画面の更新に合わせて1秒に約60回くり返します。

準備:Claude Codeと、ゲームを開くためのローカルサーバー

Claude Codeは、ターミナルで動くAnthropicのコーディングエージェントです。npm install -g @anthropic-ai/claude-code 等でインストールし、作業フォルダで claude と打つと起動します(利用プラン・料金は公式サイトで確認してください)。

ターミナルbash
# ゲーム用のフォルダを作り、Gitで履歴を残せるようにしてからClaude Codeを起動
mkdir block-game && cd block-game
git init
claude

最初の1ファイル版は、HTMLファイルをダブルクリックしてブラウザで開くだけでも動きます。ただし後半のPWA(service worker)は file:// では動かないので、ローカルサーバーで開く習慣をつけておきましょう。コマンドはAIに「このフォルダをローカルサーバーで開くコマンドを教えて」と聞けば出してくれます。

ターミナル(どちらか一方)bash
# Node.jsがある場合(表示されたURLをブラウザで開く)
npx serve .
# Pythonがある場合 → http://localhost:8000
python3 -m http.server 8000
Cursor・Codexなど他のAIエージェントでも同じ流れでOK ファイルを作り、コマンドを実行できるコーディングエージェントなら、この記事のプロンプトはほぼそのまま使えます。ゲームは完成物を目で見て判断できるので、AIエージェント開発の入門にも最適です。HTML・CSS・JavaScriptの役割分担に不安があれば、IT基礎シリーズ 第2章 フロントエンド入門(シリーズ全体はIT基礎ロードマップ)を先に読むと理解が早まります。

STEP1 遊びの仕様を言葉で伝える

ゲーム作りでも、最初の指示は「ブロック崩しを作って」だけにしないのがコツです。AIはそれっぽいものを作りますが、画面の向き・操作方法・勝ち負けの条件が自分のイメージと違うと、後から直す量が増えます。遊びのルールを、友だちに説明するつもりで箇条書きにします。

プロンプト①:遊びの仕様を伝えるClaude Code
ブラウザで遊べるブロック崩しを作りたいです。仕様:
- 縦長画面(スマホを縦に持って遊ぶ想定)。PCでも遊べる
- 画面下のパドルをマウス(あとで指)で左右に動かす
- ボールでブロックを壊すと10点。全部壊したら少し速くなって次の面へ
- ボールを3回落としたらゲームオーバー。タップ/クリックで再スタート
- 画面上部に SCORE と残りライフを表示
- 見た目はシンプルでOK(暗い背景に水色のブロック)

まず、この仕様で分かりにくい点や、決めておいた方がいい点があれば質問してください。

Claude Codeの質問:「パドルの端で打ったときに反射角を変えますか(変えると狙って打てるようになります)?」「ボールの速さは画面の大きさに合わせますか?」「1面のブロックの数は?」といった、遊び心地に直結する点を聞き返してきました。「端で打つほど斜めに飛ぶように」「ブロックは6列×5段」と答えます。

助手アイ
助手アイ反射角なんて考えてもいませんでした…!言われてみると、それがないと「狙って打つ」楽しさがないですね
エージェント博士
エージェント博士そうじゃ、AIは「よくあるゲームの作り方」をたくさん知っておる。質問させると、自分が決め忘れていた点をAIが見つけてくれるんじゃ。遊びの肝はあなたが決め、定番の作法はAIに教わる。これが良い分担じゃぞ

STEP2 1ファイルで動くものを作らせて、遊ぶ

プロンプト②:まず1ファイルでClaude Code
回答した仕様で、index.html 1ファイルだけで動くものを作ってください。
- 外部ライブラリは使わず、HTML5 Canvas と素のJavaScriptで
- 速さ・パドルの幅・ブロックの数などの数値は、コードの先頭近くにまとめて
- 端末の性能によってボールの速さが変わらないように、経過時間で動かして
作ったら、ブラウザでの開き方を教えてください。

Claude Codeがしたこと:index.html を1つ作成。中に <canvas> と <script> があり、ゲームループ・当たり判定・スコア表示までが入っています。開き方として「ダブルクリック、または npx serve .」を案内してくれました。

画面の結果:ブラウザで開くと、暗い背景に水色のブロックが並び、クリックでボールが動き出しました。この時点で一度 git commit。ここから先、何を頼んでも「遊べる状態」にいつでも戻れます。

なぜ最初は「1ファイル」なのか ファイルが1つなら、AIも人も全体を一度に見渡せ、「どこで何をしているか」を確認しやすいからです。ファイル分けやPWA化は、遊んで面白いと分かってからで十分。作り込む前に「そもそも楽しいか」を確かめるのが、ゲーム作りの鉄則です。

STEP3 「難しすぎる」「効果音を」「スコアを保存」

実際に遊ぶと、感想が次々に出てきます。これをそのままAIに伝えます。ポイントは「どう感じたか」と「どうなってほしいか」をセットで言うことです。

プロンプト③:遊んだ感想を修正依頼にClaude Code
遊んでみた感想です。3つ直してください。
1. 難しすぎる:ボールが速くて、1面の途中で終わってしまう。
   最初はもっとゆっくりにして、面が進むごとに少しずつ速くして。
   ついでにボールがブロックをすり抜けることがあるので原因も調べて。
2. 効果音がほしい:パドルに当たった音、ブロックを壊した音、落とした音。
   音声ファイルは用意したくないので、プログラムで音を作って。
3. ハイスコアを保存したい:ページを閉じても残るように。
変更したら、何をどう変えたか箇条書きで教えてください。

Claude Codeがしたこと:

  • 難易度:初速を下げ、全ブロックを壊すたびに速度を1.1倍にする処理を追加。すり抜けの原因は「ボールが1コマで進む距離がブロックの厚みより大きくなる瞬間がある」ことで、1コマの経過時間に上限を設けて対処(タブを切り替えて戻った直後などに、経過時間が大きくなりボールがワープしていた)
  • 効果音:音声ファイルを使わず、ブラウザのWeb Audio APIで「ピッ」という電子音を合成。当たった物ごとに音の高さを変える
  • ハイスコア:ブラウザの保存領域 localStorage に記録し、画面上部に BEST として表示

画面の結果:1面をクリアできるようになり、ブロックを壊すと気持ちいい音が…と思ったら、最初の数秒だけ音が鳴らないという問題が。これは不具合ではなく、ブラウザの「自動再生制限」によるものでした(つまずきポイントで解説)。

数値の調整は、AIに頼むより自分で触るほうが速いことも 「コードの先頭に数値をまとめて」と頼んでおいたので、vx: 150, vy: -260 のような初速やパドルの幅 w: 80 を自分で書き換えて、ブラウザを再読み込みするだけで試せます。ゲームの“手触り”は、数字を少しずつ変えて遊んで決めるものです。

STEP4 スマホのタッチ操作に対応させる

PCで楽しく遊べたら、スマホでも試します。同じWi-FiにつながったスマホからPCのローカルサーバーを開くと(npx serve が表示する「Network」のURLなど)、次の問題が出ました。

  • パドルを指で動かそうとすると、画面ごとスクロールしてしまう
  • 素早く2回タップすると画面が拡大される
  • 文字やブロックが少しぼやけて見える
プロンプト④:スマホ対応+ファイル分割Claude Code
スマホで遊ぶと次の問題があります。直してください。
- パドルを指で動かすと画面がスクロールする
- ダブルタップで拡大される
- 文字やブロックが少しぼやける
マウスと指の両方で同じように操作できるようにしてください。
画面サイズが変わっても縦横比を保って、画面いっぱいに表示して。

あわせて、このあとPWA化するので、JavaScriptを game.js に分けてください。
(動作が変わらないことを確認してから分けて)

Claude Codeがしたこと:マウスとタッチを別々に書くのではなく、両方をまとめて扱えるPointer Eventsに統一。CanvasにCSSの touch-action: none を指定してスクロールとズームを止め、ページ全体の引っ張りスクロールも overscroll-behavior: none で抑制しました。ぼやけは、スマホの高精細な画面(1つの点を2〜3個の実ピクセルで表示する)に合わせてCanvasの実解像度を上げることで解消。最後に index.html と game.js に分割しました。

画面の結果:指でパドルがなめらかに動き、スクロールも拡大も起きなくなりました。ここでもコミット。

STEP5 PWA化:ホーム画面に追加&オフライン

PWA(Progressive Web App)は、Webページを「アプリのように」使えるようにする仕組みです。アプリストアを通さずに、スマホのホーム画面にアイコンを置き、全画面で起動し、オフラインでも動かせます。必要なのは次の3つです。

必要なもの役割
manifest.webmanifestアプリの名前・アイコン・起動時の表示方法(全画面など)を書いた「名札」
sw.js(service worker)ページの裏で動く小さなプログラム。ファイルを端末に保存(キャッシュ)し、オフライン時に代わりに返す「留守番係」
HTTPSでの公開service worker はHTTPSでしか動かない(開発用の localhost だけは例外)
プロンプト⑤:PWA化Claude Code
このゲームをPWAにして、スマホのホーム画面に追加でき、オフラインでも遊べるようにしてください。
- manifest.webmanifest と sw.js を作成し、index.html から読み込む
- 縦画面固定、全画面表示、背景色はゲームと同じ #101522
- アイコン(192px と 512px のPNG)は、単色の背景にブロックの絵を描いて
  PNGとして保存できる小さなHTMLツールを作ってください(画像は自分で保存します)
- ファイルを更新したとき、古いキャッシュが残り続けないようにして
最後に、Chromeの開発者ツールでPWAとして認識されているか確認する手順を教えてください。

Claude Codeがしたこと:manifest.webmanifest・sw.js を作成し、index.html にmanifestの読み込みとservice workerの登録を追加。アイコンは、Canvasで描いた絵をPNGとしてダウンロードできる make-icon.html を作ってくれたので、それを開いて2サイズを保存しました(手持ちの画像編集ツールや画像生成AIで用意してもOKです)。確認手順として、Chromeの開発者ツールの「Application」タブで Manifest と Service workers の状態を見る方法を案内してくれました。

公開とホーム画面への追加:スマホで実際にインストールするにはHTTPSで公開する必要があります。静的ファイルだけなので、GitHub PagesやCloudflare Pagesなどの静的ホスティングにそのまま置けます(公開手順は🌱初級:自分のサイトを作って公開するで解説)。公開したURLをスマホで開き、Androidの Chrome ならメニューの「ホーム画面に追加」(または表示されるインストール案内)、iPhoneの Safari なら共有ボタンから「ホーム画面に追加」を選びます(メニュー名はOSやブラウザのバージョンで変わります)。

画面の結果:ホーム画面のアイコンから起動すると、アドレスバーのない全画面でゲームが始まり、機内モードにしても遊べることを確認できました。

生成された主要コードの解説

最終的な game.js から、ゲームの骨格となる部分を抜粋して解説します。全体でも200行に満たない、素のJavaScriptです。

① 準備:座標系と高解像度対応

game.js(冒頭)JavaScript
// game.js:ブロック崩しの本体
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");
const W = 360, H = 640; // ゲーム内の座標系(画面サイズに関係なくこの大きさで考える)

// 高解像度(Retina)画面でもぼやけないように実ピクセルを増やす
function resize() {
  const dpr = window.devicePixelRatio || 1;
  canvas.width = W * dpr;
  canvas.height = H * dpr;
  ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
}
resize();
window.addEventListener("resize", resize);

const paddle = { x: W / 2 - 40, y: H - 60, w: 80, h: 12 };
const ball = { x: W / 2, y: H - 80, r: 7, vx: 150, vy: -260 }; // 速度は「1秒あたりのピクセル」
let blocks = [];
let score = 0;
let lives = 3;
let state = "title"; // title → playing → gameover

ゲーム内では常に「幅360×高さ640」の座標で考え、実際の画面サイズへの拡大縮小はCSSに任せています。こうすると、スマホでもPCでも当たり判定の計算が同じになります。devicePixelRatio(1つの点を何個の実ピクセルで表示するか)ぶんCanvasの実サイズを増やすのが、ぼやけ対策です。速度を「1秒あたりのピクセル」で持っているのは、後述の経過時間 dt を掛けて動かすためです。

② ゲームループ(requestAnimationFrame)

game.js(ゲームループ)JavaScript
// ゲームループ:画面の更新タイミング(通常60回/秒)に合わせて呼ばれる
let last = 0;
function loop(now) {
  const dt = Math.min((now - last) / 1000, 0.033); // 経過秒。タブ復帰時の大ジャンプを防ぐ
  last = now;
  if (state === "playing") update(dt);
  draw();
  requestAnimationFrame(loop);
}

requestAnimationFrame は「次に画面を描き直すタイミングでこの関数を呼んで」とブラウザに頼む命令です。setInterval と違い、画面の更新に同期し、タブが裏に回ると自動で止まるので電池にも優しい。画面の更新回数は端末によって60回/秒だったり120回/秒だったりするため、前回からの経過秒 dt に比例して動かすことで、どの端末でも同じ速さになります。Math.min(..., 0.033) は、タブ復帰直後に dt が巨大になってボールがワープする(=ブロックをすり抜ける)のを防ぐ上限です。

③ 当たり判定(円と四角)

game.js(当たり判定)JavaScript
// 円(ボール)と四角(ブロック)の当たり判定:四角の中でボールの中心に一番近い点を探す
function hitCircleRect(c, r) {
  const nearestX = Math.max(r.x, Math.min(c.x, r.x + r.w));
  const nearestY = Math.max(r.y, Math.min(c.y, r.y + r.h));
  const dx = c.x - nearestX;
  const dy = c.y - nearestY;
  return dx * dx + dy * dy <= c.r * c.r;
}

ボール(円)とブロック(四角)が重なっているかは、「四角の中で、円の中心にいちばん近い点」を求め、その点と中心の距離が半径以下かで判定します。Math.max と Math.min で座標を四角の範囲に押し込めば、その「いちばん近い点」が求まります。距離の比較は平方根を取らず2乗のまま比べるのが定番の書き方です。

④ 更新処理(反射・得点・ライフ)

game.js(update)JavaScript
function update(dt) {
  ball.x += ball.vx * dt;
  ball.y += ball.vy * dt;

  // 壁で跳ね返る(めり込んだ分は押し戻す)
  if (ball.x < ball.r) { ball.x = ball.r; ball.vx *= -1; }
  if (ball.x > W - ball.r) { ball.x = W - ball.r; ball.vx *= -1; }
  if (ball.y < ball.r) { ball.y = ball.r; ball.vy *= -1; }

  // パドル:当たった位置で反射角を変える(端で打つほど斜めに飛ぶ)
  if (ball.vy > 0 && hitCircleRect(ball, paddle)) {
    const hit = (ball.x - (paddle.x + paddle.w / 2)) / (paddle.w / 2); // -1〜1
    const speed = Math.hypot(ball.vx, ball.vy);
    ball.vx = speed * hit * 0.8;
    ball.vy = -Math.sqrt(speed * speed - ball.vx * ball.vx);
    ball.y = paddle.y - ball.r;
    sound(440, 0.05);
  }

  for (const b of blocks) {
    if (b.alive && hitCircleRect(ball, b)) {
      b.alive = false;
      ball.vy *= -1;
      score += 10;
      sound(880, 0.08);
      break; // 1フレームで壊すのは1個まで(2個同時だと反射が打ち消し合う)
    }
  }
  if (blocks.every((b) => !b.alive)) {
    makeBlocks();
    ball.vx *= 1.1;
    ball.vy *= 1.1; // 全部壊したら次の面は少し速く
  }

  // 下に落ちたらライフを減らす
  if (ball.y > H + ball.r) {
    lives -= 1;
    sound(120, 0.3);
    if (lives <= 0) {
      state = "gameover";
      saveHighScore(score);
    } else {
      resetBall();
    }
  }
}

パドルの当たった位置(中央からのずれ)で横方向の速度を決め、速さの合計は変えずに角度だけを変えるのが、STEP1で決めた「端で打つほど斜めに飛ぶ」の実装です。壁にめり込んだボールを押し戻しているのは、めり込んだままだと毎コマ反射して壁に張り付くバグになるためです。ブロックとの衝突で break しているのは、2つのブロックに同時に当たったときに反射が2回起きて打ち消し合い、ボールが突き抜けるのを防ぐためです。

⑤ 操作・効果音・ハイスコア保存

game.js(操作)JavaScript
// 操作:マウスもタッチもPointer Eventsでまとめて扱う
function toGameX(clientX) {
  const rect = canvas.getBoundingClientRect();
  return ((clientX - rect.left) / rect.width) * W;
}
canvas.addEventListener("pointermove", (e) => {
  paddle.x = Math.max(0, Math.min(W - paddle.w, toGameX(e.clientX) - paddle.w / 2));
});
canvas.addEventListener("pointerdown", (e) => {
  paddle.x = Math.max(0, Math.min(W - paddle.w, toGameX(e.clientX) - paddle.w / 2));
  // 音はユーザー操作の中で初めて鳴らせる(自動再生制限)
  if (!audio) audio = new AudioContext();
  if (audio.state === "suspended") audio.resume();
  if (state !== "playing") {
    score = 0;
    lives = 3;
    makeBlocks();
    resetBall();
    state = "playing";
  }
});

画面上の指の位置(clientX)を、Canvasの表示サイズとの比率でゲーム内座標に変換しています。Pointer Eventsはマウス・指・ペンを同じイベントで扱えるので、PCとスマホで別々のコードが不要です。

game.js(効果音)JavaScript
// 効果音:Web Audio APIで「ピッ」を合成(音声ファイル不要)
let audio = null;
function sound(freq, sec) {
  if (!audio) return;
  const osc = audio.createOscillator();
  const gain = audio.createGain();
  osc.frequency.value = freq;
  gain.gain.setValueAtTime(0.2, audio.currentTime);
  gain.gain.exponentialRampToValueAtTime(0.001, audio.currentTime + sec);
  osc.connect(gain).connect(audio.destination);
  osc.start();
  osc.stop(audio.currentTime + sec);
}

Web Audio APIの オシレーター(発振器)で指定の高さの音を作り、音量を短時間で小さくすることで「ピッ」という音にしています。音声ファイルが要らないので、PWAでキャッシュするファイルも増えません。AudioContext を最初のタップの中で作っているのが、自動再生制限への対策です。

game.js(ハイスコア保存)JavaScript
// ハイスコア保存(プライベートブラウズ等で使えない場合もあるのでtry/catch)
const KEY = "blockgame.highscore";
function loadHighScore() {
  try { return Number(localStorage.getItem(KEY)) || 0; } catch { return 0; }
}
function saveHighScore(s) {
  try { if (s > loadHighScore()) localStorage.setItem(KEY, String(s)); } catch { /* 保存できなくても遊べる */ }
}

localStorage はブラウザごと・サイトごとに文字列を保存できる領域です。プライベートブラウズや設定によっては使えない(例外が発生する)ことがあるため、try/catch で囲み、保存できなくてもゲーム自体は遊べるようにしています。保存先はその端末のそのブラウザだけなので、別の端末とは共有されません(ランキングを共有するにはサーバーが必要)。

⑥ index.html と PWAのファイル

index.htmlHTML
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <!-- ピンチで拡大されないようにする(ゲーム操作と干渉するため) -->
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <title>ぽこぽこブロック崩し</title>
  <link rel="manifest" href="manifest.webmanifest">
  <meta name="theme-color" content="#101522">
  <link rel="apple-touch-icon" href="icon-192.png">
  <style>
    html, body { margin: 0; height: 100%; background: #101522; overscroll-behavior: none; }
    body { display: flex; justify-content: center; align-items: center; }
    /* touch-action: none でキャンバス上のスクロール・ズームを止める */
    canvas { width: min(100vw, 56.25vh); aspect-ratio: 9 / 16; touch-action: none; }
  </style>
</head>
<body>
  <canvas id="game"></canvas>
  <script src="game.js"></script>
  <script>
    if ("serviceWorker" in navigator) {
      navigator.serviceWorker.register("sw.js");
    }
  </script>
</body>
</html>
manifest.webmanifestJSON
{
  "name": "ぽこぽこブロック崩し",
  "short_name": "ブロック崩し",
  "start_url": "./",
  "scope": "./",
  "display": "standalone",
  "orientation": "portrait",
  "background_color": "#101522",
  "theme_color": "#101522",
  "icons": [
    { "src": "icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "icon-512.png", "sizes": "512x512", "type": "image/png" },
    { "src": "icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
  ]
}

display: "standalone" でアドレスバーのない全画面表示に、purpose: "maskable" のアイコンはAndroidで丸や角丸に切り抜かれても見栄えがよいように指定するものです(切り抜かれても大事な絵が欠けないよう、余白を多めに描いておく)。iPhone向けには apple-touch-icon も指定しています。

sw.jsJavaScript
// sw.js:オフラインでも遊べるようにファイルをキャッシュする
const CACHE = "blockgame-v3"; // ファイルを更新したら番号を上げる
const FILES = ["./", "./index.html", "./game.js", "./manifest.webmanifest", "./icon-192.png", "./icon-512.png"];

self.addEventListener("install", (event) => {
  event.waitUntil(caches.open(CACHE).then((cache) => cache.addAll(FILES)));
  self.skipWaiting();
});

// 古いバージョンのキャッシュを削除
self.addEventListener("activate", (event) => {
  event.waitUntil(
    caches.keys().then((keys) =>
      Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k)))
    )
  );
  self.clients.claim();
});

// キャッシュにあればそれを返し、なければネットワークへ(キャッシュ優先)
self.addEventListener("fetch", (event) => {
  if (event.request.method !== "GET") return;
  event.respondWith(
    caches.match(event.request).then((hit) => hit || fetch(event.request))
  );
});

service worker は、install(初回に必要なファイルを保存)→ activate(古いキャッシュを削除)→ fetch(ページがファイルを要求するたびに、保存済みなら端末から返す)の3つのイベントで動きます。この「キャッシュ優先」方式はオフラインに強い反面、ファイルを更新してもキャッシュ名(blockgame-v3)を変えない限り古いままになる点に注意してください。更新のたびに番号を上げるのを忘れないよう、CLAUDE.mdに「game.jsやindex.htmlを変更したら sw.js の CACHE の番号を上げる」と書いておくと、AIも守ってくれます。

つまずきポイントとAIへの直し方の頼み方

音が鳴らない(自動再生制限)

ブラウザは、ユーザーが何も操作していないのに音が鳴るのを防ぐため、タップやクリックなどの操作があるまで音を出させません。ページを開いた瞬間に AudioContext を作ると「停止中(suspended)」の状態になり、音が出ないのです。対策は「最初のタップの処理の中で作る、または resume() する」。ゲームを「タップしてスタート」で始める作りにしておくと、自然に解決します。iPhoneではマナーモード(消音スイッチ)の状態によってWebの音が鳴らないこともあるので、実機で確認しましょう。

頼み方の例Claude Code
スマホで効果音が鳴らないことがあります。
ブラウザの自動再生制限が原因か確認して、最初のタップで音声を有効にする形に直してください。
iPhone の Safari と Android の Chrome の両方で鳴るようにしたいです。

スマホで画面がスクロール・拡大してしまう

ゲーム中の指の動きを、ブラウザが「スクロールしたい」「拡大したい」と解釈してしまう現象です。Canvasに touch-action: none、ページに overscroll-behavior: none を指定するのが基本の対策です。タッチイベント(touchmove)で preventDefault() を使う古い書き方をAIが出してきた場合、イベント登録に { passive: false } が必要になるなど細かい落とし穴があるので、「Pointer Eventsとtouch-actionで対応して」と方針を指定すると確実です。

その他のよくある症状

症状よくある原因AIへの頼み方
更新したのに、スマホでは古いままのゲームが動くservice worker のキャッシュが古い「sw.js のキャッシュ名を上げて。開発中にキャッシュを無視して確認する方法も教えて」(Chromeの開発者ツールの Application → Service workers → Update on reload 等)
PWAとして認識されない・インストールできないHTTPSでない、manifestのアイコン指定の誤り、file:// で開いている「開発者ツールの Application タブの警告文を貼ります。原因を特定して」
ボールが壁やパドルに張り付くめり込んだ状態で毎コマ反射している「衝突時にボールを外側に押し戻す処理があるか確認して」
速い端末だとボールが速い経過時間ではなく「1コマあたり◯px」で動かしている「requestAnimationFrameの経過時間で移動量を計算する形に直して」
iPhoneでホーム画面から起動するとハイスコアが消えているホーム画面から起動したアプリとSafariで保存領域が別扱いになる場合がある仕様として説明に書く。どうしても共有したい場合はサーバーでの保存が必要
エージェント博士
エージェント博士ゲームは「PCでは動くのにスマホではダメ」が本当に多い。AIはあなたのスマホを触れんから、実機で遊んで、症状を具体的に伝えるのが人の大事な仕事じゃ。スクショや画面録画を見せるのも効果的じゃぞ

応用アイデア:子どもと作る・社内イベント用クイズ

子どもと一緒に作る

ゲーム作りは、子どもにとって「ルールを言葉にする」「試して直す」を体験できる格好の教材です。子どもが「ボールが2つになったら面白い!」「ブロックの色を虹色に」とアイデアを出し、大人がそれをAIへの指示に整えて入力する、という役割分担がおすすめです。「なぜ速くなりすぎたのかな?」とコードの数字を一緒に変えてみると、プログラミングの考え方にも自然に触れられます。

  • AIの画面を一緒に見ながら進め、子どもだけで長時間使わせない(各AIサービスの利用規約の年齢条件も保護者が確認)
  • 名前や写真などの個人情報をゲームやプロンプトに入れない

社内イベント用のクイズゲーム

同じ作り方で、懇親会や研修で使う早押し・4択クイズも作れます。問題をJSONファイルに分けておけば、担当者がコードを触らずに問題を差し替えられます。

プロンプト例:社内イベント用クイズClaude Code
社内の懇親会で、プロジェクターに映して遊ぶ4択クイズを作ってください。
- 問題は questions.json に分け、HTMLを触らずに差し替えられるように
- 1問20秒の制限時間とカウントダウン音、正解発表の演出
- チーム(4チーム)ごとの得点を司会が手動で加点できる画面
- ネットがない会場でも動くよう、外部ライブラリやCDNは使わない
まず画面の流れ(タイトル→問題→正解発表→結果)を提案してください。

ほかにも、研修用のタイピングゲーム(業務用語を打つ)、新人向けの社内用語当てクイズ、展示会ブースのミニゲーム(タブレットで遊んでもらう)などに応用できます。いずれも「1ファイルで作る → 遊ぶ → 感想で直す → 必要ならPWA化」の流れは同じです。

事例:AIエージェントと「作って、試して、直す」

自社事例

このサイトも「小さく作って、確認して、戻せるようにする」で運用

AIエージェント研究所(約57ページの静的サイト)は、Claude Codeとの共同作業で作成・運用しています。トップページ導線の再設計や、全56ページのヘッダー一括統一といった大きな変更もAIエージェントで行いましたが、進め方は本記事のゲーム作りと同じです。

  • 作業ブランチで変更し、人が実際の表示を確認してから取り込む(ゲームでいう「実機で遊ぶ」)
  • 小まめにコミットし、いつでも前の状態に戻せるようにする
  • フッターの流し込み(build.js)や表記チェック(keihyo-check.js)のように、毎回確認すべきことは仕組みにする

詳しくは【自社事例】AIエージェントでメディアサイトを運用する仕組みをご覧ください。

他社の公開事例と、バイブコーディングとの違い:Anthropicは、社内でClaude Codeを広く使い、Claude Code自体のコードの多くもClaude Codeで書いていると公表しています。また、2025年2月にAndrej Karpathy氏が広めた「バイブコーディング(vibe coding)」という言葉は、コードの中身を気にせず雰囲気でAIに任せきる作り方を指し、実際に小さなゲームを“雰囲気で”作って楽しむ人も増えています。遊びの試作ならそれも一つの楽しみ方ですが、人に遊んでもらう・社内イベントで使うなら、本記事のように「仕様を伝え、実機で確かめ、直してもらう」進め方のほうが、本番で「音が鳴らない」「スマホで動かない」と慌てずに済みます。

よくある質問

ゲームエンジン(UnityやPhaserなど)を使わなくても大丈夫ですか?

ブロック崩しやクイズのような2Dの小さなゲームなら、HTML5 Canvasと素のJavaScriptで十分に作れます。ファイルが少なく、AIも人も全体を把握しやすいのが利点です。物理演算が多い、画面やキャラクターが多いなど規模が大きくなってきたら、Phaserなどのゲーム用ライブラリの利用をAIに相談するとよいでしょう。

PWAにすればアプリストアに出したアプリと同じですか?

ホーム画面から全画面で起動し、オフラインでも動くという点ではアプリに近い体験になりますが、同じではありません。アプリストアで配信されるわけではなく、使える端末機能や通知などの対応状況はOSやブラウザによって異なります。まずはPWAで手軽に配り、必要になったらストア配信を検討するのが現実的です。

作ったゲームのハイスコアを友だちや社員同士で共有できますか?

この記事の作り方では、ハイスコアはその端末のブラウザ内(localStorage)にだけ保存されるため、他の人とは共有されません。ランキングを共有するには、スコアを受け取って保存するサーバーとデータベースが必要です。データ保存つきWebアプリの作り方を応用すれば実現できます。

子どもと一緒にAIエージェントでゲームを作っても大丈夫ですか?

大人が一緒に画面を見ながら進めるのがおすすめです。子どもがアイデアを出し、大人がAIへの指示に整えて入力する役割分担にすると、ルールを言葉にする力や試して直す体験が得られます。AIサービスの利用規約の年齢条件は保護者が確認し、名前や写真などの個人情報はゲームにもプロンプトにも入れないようにしてください。

まとめ|遊んだ感想が、いちばん良いプロンプトになる

ブラウザゲームは、AIエージェント開発の楽しさと進め方の基本が詰まった題材です。遊びのルールを言葉にして質問させる → 1ファイルで動かして遊ぶ → 感想をそのまま修正依頼にする → スマホの実機で確かめる → PWAで配る。この流れは、ゲーム以外の業務ツール作りにもそのまま使えます。

コードの中身も、ゲームループ・当たり判定・状態管理・キャッシュと、Web開発の大事な考え方がコンパクトに入っています。AIが書いたコードを、この記事の解説と照らし合わせながら少しずつ読んでみてください。次の記事は🌳高度編。既存コードの改修・テスト追加・大規模リファクタリングをAIエージェントに任せる方法を解説します。データを保存するアプリを作りたい方は、前の記事データ保存つきWebアプリ(Next.js+SQLite)もどうぞ。

作れるようになったら、スキルを収入につなげる

AIエージェントで「作れる」ことは、それ自体が仕事になります。案件獲得や転職、作業環境づくりに役立つ記事をまとめました。

AIが快適に動くノートパソコンの選び方&おすすめ3選【2026】開発・AIエージェントを動かすならメモリ16GB以上が目安
記事を読む

次の一歩

姉妹サイトの記事で、次に気になりやすいことを調べられます。