TL

Canvas 2DとWebGL/WebGPUのレンダリング経路の違い

図形を描くだけならCanvas 2D、大量の頂点やGPU計算が要るならWebGL/WebGPUと、迷わず選べるようになる。即時モード・状態機械・コマンドバッファという3つの描画モデルの原理から適材適所を解説します。

応用CanvasWebGLWebGPUGPUレンダリングブラウザ最終更新: 2026-06-21
TL;DR要点だけ先に
  • 1.Canvas 2Dは即時モード(命令を呼ぶたびにその場で描く)APIで、状態は2Dの描画コンテキストが持つ。手軽だが、毎フレームCPUから多数の呼び出しを発行するため大量描画には向かない。
  • 2.WebGLは巨大なグローバル状態機械で、bind→設定→drawの順で暗黙の状態を切り替えながら描く。柔軟だが状態漏れとドローコール過多が性能の壁になる。
  • 3.WebGPUは状態をパイプラインオブジェクトに固め、コマンドをバッファにまとめてからキューへ一括投入する。検証コストを前倒しし、ドローコールあたりのCPUオーバーヘッドを下げるのが設計思想。

3つのAPIは「描き方のモデル」が違う

Canvas 2D・WebGL・WebGPUはどれも <canvas> 要素に描画しますが、性能特性が大きく異なるのは、解像度やGPUの有無以前に描画コマンドをどう組み立て、どこで状態を持つかというモデルが根本から違うからです。Canvas 2Dは命令ごとに即描く即時モード、WebGLは暗黙の状態を切り替えて描くグローバル状態機械、WebGPUは状態を固めてコマンドを束ねるコマンドバッファ方式です。この3つの違いを原理で押さえると、「滑らかにならない」「ドローコールが多すぎる」といった症状を、API選択の問題として切り分けられます。最終的にどのAPIで描いた絵も、ブラウザの合成(コンポジット)段でページの他のレイヤーと重ね合わされて画面になります。

Canvas 2D:即時モードという手軽さと限界

Canvas 2Dは getContext("2d") で得る CanvasRenderingContext2D に対し、fillRectarc といったメソッドを呼ぶと**その場で(即座に)**ピクセルが塗られる、即時モード(immediate mode)のAPIです。保持モード(retained mode)のDOMやSVGと違い、描いた図形は「オブジェクト」として残らず、結果のビットマップだけが残ります。だから再描画は毎回ゼロから描き直しで、シーングラフの管理は完全にアプリ側の責任です。

状態は描画コンテキストが1セットだけ持ちます。fillStylelineWidth・現在の変換行列(CTM)・クリップ領域などはコンテキストのプロパティで、save()/restore() で状態スタックに退避・復元します。次のコードは「状態を変えてから描く」という即時モードの基本形です。

const ctx = canvas.getContext("2d");
ctx.save();
ctx.translate(100, 50);        // 現在の変換行列を更新
ctx.fillStyle = "tomato";      // 以降の塗り色を設定
ctx.fillRect(0, 0, 80, 80);    // この時点で即描画される
ctx.restore();                 // 変換・色などを退避前へ戻す

性能の限界は、描画1回ごとにCPUからAPI呼び出しが発生する点にあります。実装はGPU合成されることも多いものの、APIの粒度は1図形単位で、数万個の図形を毎フレーム個別に呼べばCPU側のコマンド発行がボトルネックになります。三角形を大量に流す・シェーダで陰影を計算するといった用途には構造的に向きません。逆に、図形数が限られたチャート・注釈・2Dゲームのスプライト程度なら、最小の学習コストで十分な性能が出ます。

Canvas 2Dを速く使うコツ

状態変更(特に fillStyle やフォントの切り替え)は呼び出しコストが高いので、同じ状態の図形をまとめて描く。動かない背景は別の OffscreenCanvas に一度描いてキャッシュし、毎フレームは差分だけ描く。save()/restore() の多用も状態スタック操作を増やすため、必要最小限にします。

WebGL:グローバル状態機械の柔軟さと罠

WebGLはOpenGL ES 2.0/3.0をブラウザに移したAPIで、本質は巨大なグローバル状態機械です。描画は「描く対象を直接指定する」のではなく、bind(束縛)でカレントな対象を切り替え→各種パラメータを設定→drawArrays/drawElements で発射という手順を踏みます。何を描くかは、その瞬間にバインドされているバッファ・テクスチャ・シェーダプログラムという暗黙の状態で決まります。

gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);   // カレントの頂点バッファを切り替え
gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
gl.useProgram(program);                       // カレントのシェーダを切り替え
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.drawArrays(gl.TRIANGLES, 0, vertexCount);  // 現在の状態一式で描画

GPU上で動くのは2種類のシェーダです。頂点シェーダが頂点ごとに座標変換を行い、ラスタライザがプリミティブをピクセルに分解し、フラグメントシェーダがピクセルごとの色を計算します。頂点データは ArrayBuffer 由来のTypedArrayとしてGPUへアップロードします。この仕組みにより、数十万頂点の変換やピクセル単位の陰影計算をGPUへ丸投げでき、Canvas 2Dでは不可能な規模の描画が可能になります。

罠は2つあります。第一に状態漏れで、どこかでバインドしたまま戻し忘れた状態が次の描画に影響し、再現の難しいバグになります。第二にドローコール過多です。draw* 一発ごとにブラウザ(と裏のドライバ)が状態の妥当性を検証するため、CPUオーバーヘッドが無視できません。

性能を決めるのは頂点数よりドローコール数

GPUは大量の頂点を並列処理するのが得意な一方、ドローコール1回ごとのCPU側の準備コストは固定的にかかります。同じ材質の多数オブジェクトは、インスタンシング(drawArraysInstanced)や頂点バッファの結合で1回のdrawにまとめるのが定石です。「重い」原因が頂点数だと思い込むと、まとめる発想に至れません。

WebGPU:パイプラインとコマンドバッファ

WebGPUは状態機械の弱点を、設計レベルで解消するために作られた新しいAPIです(背後はVulkan/Metal/Direct3D 12)。中核は2つの考え方です。

ひとつはレンダーパイプラインオブジェクト。シェーダ・頂点レイアウト・ブレンド設定・出力フォーマットといった「描画の構成」を、描画前に createRenderPipeline1つのイミュータブルなオブジェクトに固めて検証します。WebGLが描画のたびに散在する状態を都度検証していたのに対し、WebGPUは検証を構築時へ前倒しし、描画ループでは完成済みパイプラインを差すだけにします。

もうひとつがコマンドバッファです。描画命令は即実行されず、CommandEncoder に記録されてコマンドバッファになり、queue.submit で初めてGPUへ一括投入されます。即時モードのCanvas 2Dとは対照的に、命令の「記録」と「実行」が明確に分離されています。

const encoder = device.createCommandEncoder();
const pass = encoder.beginRenderPass(renderPassDescriptor);
pass.setPipeline(pipeline);     // 検証済みパイプラインを差す
pass.setBindGroup(0, bindGroup);
pass.draw(vertexCount);         // ここではまだ記録するだけ
pass.end();
device.queue.submit([encoder.finish()]); // バッファをまとめてGPUへ

リソースはバインドグループとしてまとめて束ね、setBindGroup で一括切り替えします。WebGLの個別バインドの集積を、整理された単位に置き換えた形です。さらにWebGPUはコンピュートシェーダdispatchWorkgroups)を標準で持ち、描画を伴わない汎用GPU計算(GPGPU)——物理シミュレーション・画像処理・機械学習——を、WebGLのような描画への偽装なしに書けます。

観点Canvas 2DWebGLWebGPU
描画モデル即時モード(呼ぶ=描く)グローバル状態機械(bind→draw)パイプライン+コマンドバッファ
状態の持ち方1つの2Dコンテキスト暗黙のグローバル状態パイプライン/バインドグループに固定
検証のタイミング呼び出しごとdrawごとに都度パイプライン構築時に前倒し
GPU計算なし描画への偽装で限定的にコンピュートシェーダで正式に
向く用途図形・チャート・2D注釈3D・大量頂点・既存資産高負荷3D・GPGPU・最新性能
用語の整理

「即時モード」は命令ごとに即描くCanvas 2D、「保持モード」は描画対象をオブジェクトとして保持するDOM/SVGの方式です。WebGLの「状態機械」とWebGPUの「コマンドバッファ」は別概念で、前者は暗黙状態の切り替えで描き、後者は命令を記録してから一括実行します。WebGPUが速くなり得る主因はGPUが新しいからではなく、ドローコールあたりのCPUオーバーヘッドと検証コストを下げる構造にあります。

適材適所:何を基準に選ぶか

選択は「新しいから」「速そうだから」ではなく、描画の規模とGPU計算の要否で決めます。図形数が限られ2Dで足りるならCanvas 2Dが最も手軽で、学習・保守コストも最小です。3Dや大量頂点が必要で、広い実行環境への対応や既存ライブラリ(多くは内部でWebGLを使う)を優先するならWebGLが現実解です。極端に高い描画負荷、明示的なGPU計算(GPGPU)、最新世代の性能を引き出したい場合はWebGPUが適します。

WebGPUは新しいぶん、対応ブラウザの確認とフォールバック設計(WebGLや2Dへの代替)が要ります。いずれを選んでも、最終的な体感速度はメインスレッドを詰まらせないことに左右されるため、重い前処理はWebパフォーマンスの観点でワーカーへ逃がし、描画はブラウザのレンダリングパイプラインのフレーム予算(おおむね16.6ms)に収める設計が前提になります。

まとめ

3つのAPIの差は解像度やGPUの有無ではなく描画コマンドの組み立て方です。Canvas 2Dは即時モードで手軽だがCPUからの呼び出し過多が壁。WebGLはグローバル状態機械で柔軟だが状態漏れとドローコール過多が壁。WebGPUは状態をパイプラインに固め、コマンドをバッファに束ねて検証を前倒しし、ドローコールあたりのコストとGPGPUの書きやすさで優位に立ちます。2Dで足りるならCanvas 2D、3D・大量頂点ならWebGL、最高負荷とGPU計算ならWebGPU——モデルの違いを理解すれば、症状からAPI選択を逆算できます。

Web/フロントエンド Article

Canvas 2DとWebGL/WebGPUのレンダリング経路の違いを実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

Canvas

比較で見る軸

難易度: advanced / カテゴリ: Web/フロントエンド / タグ数: 6

導入後に効く点

WebGLは巨大なグローバル状態機械で、bind→設定→drawの順で暗黙の状態を切り替えながら描く。柔軟だが状態漏れとドローコール過多が性能の壁になる。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
advanced
カテゴリ
Web/フロントエンド
タグ数
6

判断チェックリスト

  • 自社の用途が「Canvas / WebGL」に近いか確認する。
  • 強みである「Canvas 2Dは即時モード(命令を呼ぶたびにその場で描く)APIで、状態は2Dの描画コンテキストが持つ。手軽だが、毎フレームCPUから多数の呼び出しを発行するため大量描画には向かない。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

CanvasWebGLWebGPUGPUレンダリングCanvasWebGLWebGPU
参考: 公式情報