開発記録|1本のHTMLでブラウザゲームを作った話

2026年7月

公開: 2026年7月31日 ・ 最終更新: 2026年8月22日 ・ 筆者: Toshi(開発者)

バズ走は、HTMLファイル1本だけで動くブラウザゲームです。外部のゲームエンジンもライブラリも使っていません。ここでは、どういう考えで作ったのかを開発メモとして残しておきます。同じようにブラウザゲームを作ってみたい方の参考になれば幸いです。

設計方針:とにかく「踏んだ瞬間に遊べる」こと

最初に決めたのは、リンクを踏んでから遊び始めるまでの時間を限界まで短くするという一点でした。スマートフォンで暇つぶしのゲームを探している人にとって、アプリのインストールやアカウント登録は大きな壁です。ロード画面すら離脱の原因になります。

そこで、次の制約を自分に課しました。

結果として、ゲーム本体は数十KBに収まりました。回線が細い環境でも、タップした次の瞬間には走り始められます。

グラフィックは「描かずに描く」

画像ファイルを一切使わない方針なので、背景も棒人間もすべて Canvas API のコードで描いています。円弧、直線、二次ベジェ曲線の組み合わせだけで、東京タワー、五重塔、ピラミッド、エッフェル塔、自由の女神といったランドマークを表現しました。

この制約は不便なようでいて、実は作品の統一感を生む要因にもなりました。手描き風の線と、白い紙のような背景、差し色の赤だけ。素材を集める時間もゼロで、あとから都市を追加するのもコードを数十行足すだけで済みます。

実装メモ:落書き感の出し方
太陽は「わずかに中心をずらした円弧を3本重ねる」だけで、手でぐるぐる描いたような質感になります。完全な円を1本描くより、ずれた線を重ねる方が手描きらしく見える、というのは他の描画にも応用しました。

難易度は「運」ではなく「腕」で決まるようにした

初期版は地形を完全なランダムで生成していました。しかし何度もテストするうちに、明らかな問題が見えてきました。同じ腕前でも、地形の引き次第でスコアが数倍変わるのです。これでは記録に意味がなく、友達とスコアを比べても盛り上がりません。

そこで、日付から擬似乱数の種を作る方式に変更しました。これにより、同じ日のうちは世界中の全員が同じ地形を走るようになりました。運の要素が消えたことで、繰り返し走って地形を覚えるという上達の道筋も生まれました。

称号を100種類にした理由

当初、称号は6段階しかありませんでした。ところが自分で遊んでいると、何度走っても同じ称号ばかり出てきて、リザルト画面を見る楽しみが薄れていくことに気づきました。

そこで50mごとに1つ、合計100種類まで拡張しました。ポイントは、上手い人だけが報われる設計にしなかったことです。序盤の称号ほど自虐的で笑える文言にしてあります。「靴ひも結び中」「ゴミ出しダッシュ」「犬の散歩ペース(先に犬が帰った)」といった具合です。

下手なプレイほど面白い結果が出るようにしたことで、初心者でも結果を見せたくなる。この構造は、ゲームそのものより重要だったかもしれません。

スマートフォン対応で一番苦労した点

パソコンでは快適に遊べたのに、スマートフォンの縦画面ではまったく別のゲームになってしまう、という問題がありました。原因は前方の視認距離です。横に広い画面では障害物が早く見えるのに対し、縦長の画面では見えた瞬間にはもう避けられません。

解決策として、画面幅が狭いときはカメラを引いて描画する仕組みを入れました。ゲーム内部の物理計算はそのままに、見える範囲だけを約1.7倍に広げています。難易度そのものは変えていないので、端末による有利不利を最小限に抑えつつ、縦持ちでも「見てから反応できる」状態になりました。

世界一周コースという構造

距離だけを競うゲームは、数字が伸びても実感が湧きにくいという弱点があります。そこで、距離に応じて背景が切り替わる世界一周コースを用意しました。

ニューヨークから出発し、アジア、中東、ヨーロッパを巡り、成層圏を突破して月・火星・土星・太陽へ。帰りはスペイン、ポルトガル、モロッコを経由して出発地に戻る全22ステージ、1周3,550mです。

これにより「1,850m走った」ではなく「火星まで行った」と言えるようになりました。数字より場所の方が、人に伝えるときの言葉として強いというのが実感です。

使った技術のまとめ

項目採用したもの理由
描画Canvas 2D API画像素材を持たずに済み、読み込みが速い
ライブラリなし(Vanilla JS)ファイルサイズと起動速度を最優先
Web Audio API効果音も音源ファイルなしで生成
記録保存localStorageサーバー不要。個人情報も持たない
地形生成日付シード+擬似乱数全員が同じコースを走れる
共有機能Canvas → Web Share APIリザルト画像をその場で生成して共有
公開環境静的ホスティングサーバー管理が不要で、表示が速い

【追記】5人対戦をサーバーレスで作った話

公開後いちばん大きな追加が、QRコードで友達を招待して最大5人で同時に走る対戦機能です。ここでは、その裏側を少しだけ紹介します。個人開発で同じようなものを作る方の参考になれば幸いです。

リアルタイム対戦と聞くと専用サーバーが必要に思えますが、バズ走はサーバーを1台も持っていません。部屋ごとの状態管理にはCloudflare WorkersのDurable Objectsを使い、部屋コード(QRコードの中身)ごとにオブジェクトが1つ割り当てられます。参加者はWebSocketでそこに接続し、走行距離とジャンプの高さだけをやり取りします。

ここで効いてくるのが「今日のコース」の設計です。地形は日付から決まるので、対戦相手と地形を同期する必要がそもそもありません。全員の端末が同じ計算式で同じ地形を作れるため、通信するのは1人あたり毎秒十数バイト程度。無料枠で十分に運用できます。

いちばん苦労したのはWebSocketの休止(ハイバネーション)です。省コストのため接続が眠るたびにメモリが消える仕様で、当初は「2人目が入ると1人目の情報が消えている」という不可解な不具合に悩まされました。勝敗に関わる情報は必ず永続ストレージに書き、接続状態は生きているソケット一覧から毎回計算し直す、という原則に落ち着いてからは安定しています。

QRコードも外部ライブラリを使わず自作しました。リード・ソロモン誤り訂正の実装では2箇所のバグ(多項式の項順の取り違えと、フォーマット情報の縦横転置)に苦しみましたが、macOSの画像認識APIで「本当にiPhoneのカメラで読めるか」を機械的に検証しながら直しました。ゲームと同じく、QRも1本のJavaScriptに収まっています。

【追記】称号100種を4言語に翻訳した話

アクセス解析を見ていて、胸が痛くなる数字がありました。日本のプレイヤーの平均プレイ時間が17分を超えているのに、海外からの訪問者は平均3秒で帰ってしまう。理由は明白で、最初の画面に日本語が残っていたからです。せっかく来てくれたのに、遊ぶ前に閉じられてしまう。これほどもったいないことはありません。

そこで英語対応を徹底したのに続き、今回は简体中文・繁體中文・スペイン語を加えました。悩んだのは称号です。「回覧板リレー」「終電ダッシュ勢」のような日本の生活感が前提のネタは、直訳しても伝わりません。例えば「終電ダッシュ勢」は、簡体字では「末班车冲刺组(最終バス・最終電車に駆け込む人たち)」に。「駅員さんがドアを押さえた」という続きのコメントは、そのまま訳しても各国で通じることが分かったので活かしました。深夜の駅で待ってくれる誰かがいる、という情景は世界共通のようです。

簡体字と繁体字は「文字を変換すれば済む」と思われがちですが、実際は語彙が違います。ソフトウェアは大陸で「软件」、台湾で「軟體」。動画は「视频」と「影片」。今回は簡体字で全文を書いたあと、台湾向けの語彙変換を通し、さらに「靴ひもを結ぶ」の「繫」のような慣用表現を個別に確認しました。

技術的には、翻訳データを本体から分離した1つのファイル(約23KB)にまとめ、ブラウザの言語設定に応じて読み分ける方式にしました。翻訳が増えてもゲームの起動速度は変わりません。そして嬉しい副産物がひとつ。フレンド対戦は各自の端末が自分の言語で表示するだけなので、東京と台北とマドリードのプレイヤーが同じ部屋で対戦できます。言語対応がそのまま「国境を越えた対戦」になりました。

世界中の言葉のニックネームがランキングに並ぶ日を楽しみにしています。

これから

今後は、遊んでくださった方の反応を見ながら、地形パターンの追加や新しい称号の検討を進めていく予定です。更新内容はお知らせで随時ご報告します。

「1本のHTMLでどこまでできるか」という実験でもあるので、なるべくこの制約は維持したまま育てていくつもりです。

⚽ いますぐ走る