マガジン

iPhoneのSafariだけでゲームが動かない原因と対策

遊びをつくる

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

不具合の起きた順番を絵札で並べ、相手に伝えるための手紙を整える手元。
記事のテーマをもとに制作したAI生成イラスト。

「PCのChromeでは動くのに、iPhoneのSafariだけ真っ白になる」「タップしても音が出ない」「ゲームが途中で止まる」——ブラウザゲームを公開すると、こうした報告が届くことがあります。原因のほとんどはSafari固有の仕様か、iOSのセキュリティポリシーに起因するものです。ChromeやFirefoxと同じコードが動かない理由には、それぞれ明確な理由があります。 この記事では、iPhoneのSafariでゲームが動かないときに「何が起きているか」を特定する方法と、それぞれの対策を手順ごとに示します。「なぜかわからないまま試行錯誤する」状態から抜け出すことを目的にしています。

この記事の目次
  1. まず「どこで止まっているか」を切り分ける
  2. 音が出ない・AudioContextが動かない
  3. タッチ操作が効かない・スクロールと競合する
  4. WebGLが途中で止まる・コンテキストが失われる
  5. CSS・レイアウトのiOS固有のずれ
  6. 次の行動
  7. 参考資料

まず「どこで止まっているか」を切り分ける

Safariでゲームが動かないとき、問題は大きく三つの層に分かれます。どの層かを最初に見極めると、対策が絞り込まれます。

①画面が表示されない/真っ白になる:HTMLの読み込み自体か、JavaScriptの初期化で止まっています。

②表示はされるが操作できない:タッチイベントの扱い方か、audio/videoの自動再生制限が原因であることが多いです。

③途中で止まる・クラッシュする:メモリ上限への到達か、WebGLコンテキストの喪失が起きています。

iPhoneでエラーを確認するには、MacのSafariに接続してリモートデバッグを使います。MacのSafari「開発」メニューから接続中のiPhoneを選ぶと、コンソールにエラーが表示されます。手元にMacがない場合は、try/catchでエラーを画面上に表示する簡易ログを仕込む方法が代替手段になります。どの層で止まっているかをログで確認してから対策に進むと、無駄な修正を減らせます。

音が出ない・AudioContextが動かない

iOSのSafariは、ユーザー操作(タップ・クリック)を起点としない音声の再生を許可しません。これは仕様であり、コードのバグではありません。AudioContextをnew AudioContext()で作った直後に音を鳴らそうとしても、iOSでは「suspended(一時停止)」状態のままになります。

対策は、タップイベントのハンドラの中でaudioContext.resume()を呼ぶことです。

document.addEventListener('touchend', () => {
  if (audioContext.state === 'suspended') {
    audioContext.resume();
  }
}, { once: true });

<audio>タグを使っている場合も同様で、play()はユーザー操作のコールバック内で呼ぶ必要があります。「スタートボタンを押したら音楽を始める」という設計にすれば、この制限に自然に対応できます。ゲーム開始前にタップを要求する「タップしてスタート」画面は、見た目の工夫であると同時に、iOSの音声制限を回避する実用的な設計でもあります。

タッチ操作が効かない・スクロールと競合する

PCでmousedown/mouseup/mousemoveだけを使っているゲームは、iPhoneでは操作を受け付けません。iOSはマウスイベントを模倣する仕組みを持っていますが、遅延やキャンセルが発生することがあるため、touchstart/touchend/touchmoveを明示的に処理する必要があります。

加えて、Safariはデフォルトでタッチ操作をページスクロールに割り当てるため、ゲームキャンバス上でスワイプするとページが動いてしまうことがあります。これを防ぐには、ゲームのキャンバスや操作領域に対してtouchmoveイベントでpreventDefault()を呼びます。ただしaddEventListenerの第3引数に{ passive: false }を指定しないと、preventDefault()が効かない場合があります。

canvas.addEventListener('touchmove', (e) => {
  e.preventDefault();
}, { passive: false });

また、iOSのSafariにはダブルタップでページを拡大する挙動があります。ゲーム中に意図せず画面が拡大しないよう、<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">をHTMLの<head>に入れておくと安定します。

WebGLが途中で止まる・コンテキストが失われる

iPhoneは搭載RAMが限られており、Safariはメモリ不足になるとWebGLコンテキストを強制的に破棄することがあります。これが起きると、キャンバスが真っ黒になって操作を受け付けなくなります。

対策は二段階です。まずcanvas要素にwebglcontextlostイベントのリスナーを登録して、コンテキストが失われたことを検知します。次にwebglcontextrestoredイベントでテクスチャやシェーダーを再初期化する処理を書きます。

canvas.addEventListener('webglcontextlost', (e) => {
  e.preventDefault(); // 自動的な破棄を遅延させる
});
canvas.addEventListener('webglcontextrestored', () => {
  // テクスチャ・シェーダーの再初期化
  initWebGL();
});

そもそもメモリを消費しすぎないことも重要です。テクスチャは使い終わったらgl.deleteTexture()で解放する、バッファも同様に管理する、といった習慣がiOS向けには特に効いてきます。画像の解像度を必要以上に高くしないことも有効です。2048×2048ピクセルを超えるテクスチャはiOSで扱える上限に近づくため、スプライトシートの設計を見直す余地があります。

CSS・レイアウトのiOS固有のずれ

ゲームのUIがPC上では正しく表示されるのにiPhoneでずれる場合、SafariのCSSの扱いが原因であることがあります。特に注意が必要な点をいくつか挙げます。

100vh問題:iOSのSafariはアドレスバーの高さを100vhに含めることがあり、実際の表示領域より大きい値になる場合があります。height: 100dvh(dynamic viewport height)を使うと改善されますが、古いiOSバージョンはdvh未対応のため、フォールバックとして100vhを先に書いておくと安全です。

position: fixedのずれ:キーボードが表示される操作があるゲームでは、fixed要素が意図しない位置にずれることがあります。ゲーム内に文字入力がある場合は特に確認が必要です。

フォントの自動拡大:Safariはフォントサイズが小さいと判断すると自動的に拡大することがあります。-webkit-text-size-adjust: 100%をCSSに指定すると、この挙動を抑制できます。

次の行動

iPhoneのSafariで起きている問題の層(表示・操作・音・メモリ)を特定してから、この記事の対応する節を参照してください。MacとiPhoneがあればリモートデバッグでコンソールエラーを直接確認するのが最短の診断方法です。修正後は実機のSafariで必ず動作確認をおこない、iOSのバージョンが異なる端末でも確認できると安心です。アソバースにZIPまたはHTMLを投稿する場合も、公開前に実機でのチェックを習慣にすると、プレイヤーへの影響を事前に防げます。

参考資料

  • Apple Developer Documentation — WebKit
  • MDN Web Docs — Touch events
  • MDN Web Docs — Web Audio API
  • MDN Web Docs — WebGL: 2D and 3D graphics for the web