//
2026.10.02/4 min read#modern-web#design#language

WebGLを「装飾」に使うときの注意——1つのCanvasにまとめる理由

ヒーローの背景、カードのホバー、セクションの区切り。WebGLを装飾に使うと、Canvasが気づかないうちに増えていきます。私が「1ページ1Canvas」を基本にしている理由と、そのときの組み立て方を書きます。

WebGLを装飾として使うなら、私は 「1ページにCanvasは1つ」 を基本にしています。見た目の演出ごとにCanvasを置くと、ブラウザ側の上限やメモリの重複にぶつかりやすく、あとから直すのが大変だからです。演出の数ではなく、描画の入口の数を先に決めておく、というのがこの記事の結論です。

Canvasが増えると何が起きるか

WebGLは、Canvasごとに「コンテキスト」という描画の作業場を作ります。three.jsの公式マニュアルのMultiple Canvases, Multiple Scenesでは、ここに2つの問題があると説明されています。

  • ブラウザが同時に持てるWebGLコンテキストの数には上限があり、マニュアルでは「おおむね8つ程度」とされています。上限を超えると、古いコンテキストから失われます
  • WebGLのリソースはコンテキストをまたいで共有できません。同じモデルやテクスチャを2つのCanvasで使うと、2回読み込むことになります

上限の具体的な数はブラウザや端末で異なるようなので、私は数字そのものより「上限がある」「共有できない」の2点を覚えておくようにしています。

装飾のつもりで作ると、この問題は静かに起きます。ヒーローに1つ、事例カードの背景に6つ、フッターに1つ。デザインカンプの段階では「ちょっとした動き」でも、実装すると8つのCanvasになっている、ということがありえます。そして失われたコンテキストは、画面上では「なぜか一部だけ真っ白」という症状で見つかります。

1つのCanvasで複数の演出を描く

three.jsのマニュアルが示している解決策は、画面全体を覆うCanvasを背景に1枚だけ置き、各演出の場所はふつうのHTML要素で示すというものです。描画のたびに各要素の位置を調べ、画面内にあるものだけを、その位置に描きます。

ディレクションの言葉に直すと、次のような分担になります。

役割 担当
どこに演出を出すか HTMLの空の要素(位置と大きさだけ持つ)
何を描くか 1つのレンダラーと、演出ごとのシーン
いつ描くか 画面内にある要素だけを描画

この形にしておくと、演出を1つ足すときも「空の要素を1つ置いて、シーンを1つ足す」で済みます。コンテキストは1つのままなので、テクスチャやモデルも共有できます。React Three Fiberのようなライブラリを使う場合も、考え方は同じだと私は捉えています。

失われたときの備えも入れておく

1つにまとめても、コンテキストが失われる可能性はゼロにはなりません。MDNのwebglcontextlostイベントは、描画バッファが失われたことをブラウザが検知したときに発火する、と説明されています。

装飾の場合、私は凝った復帰処理よりも 「失われたらCanvasを隠して、CSSの背景に切り替える」 を優先しています。装飾が消えても本文が読めれば、ページとしては成立するからです。逆に、文字をWebGLの中だけで描いていると、失われた瞬間に情報まで消えます。見出しや本文はHTMLのまま残す、というのは装飾として使うときの前提にしています。

また、MDNのWebGLのベストプラクティスでは、不要になったコンテキストを WEBGL_lose_context で早めに手放すことも勧められています。ページ内で演出を切り替えるような構成なら、使い終わったCanvasを残さないようにしています。

動きを減らしたい人への配慮

装飾は「なくても困らない」ものなので、止められるようにしておくのが筋だと考えています。CSSのprefers-reduced-motionで、OS側で動きを減らす設定をしている人を判定できます。私はこの設定のときはアニメーションを止めて1枚の静止画として描くか、Canvas自体を出さないようにしています。

あわせて、画面外にあるときは描画ループを止めます。背景で回り続ける装飾は、スマホでは電池と発熱にそのまま響くからです。

明日から試せる一手

いま作っているページで、開発者ツールのコンソールに document.querySelectorAll('canvas').length と打ってみてください。返ってきた数が演出の数と同じなら、1つのCanvasにまとめる余地があります。デザインの段階なら、カンプに「WebGLで描く場所」を色分けして並べ、それが1枚の背景Canvasで描けるかをエンジニアと先に話しておくと、実装後の作り直しが減ります。