WebGPU vs WebGL
WebGPU is the successor to WebGL. If you’re building a graphics-heavy app in 2025+, this is the API to target.
Browser Support (2025)
| Browser | Status |
|---|---|
| Chrome/Edge 113+ | ✓ Windows, macOS, ChromeOS |
| Chrome 121+ | ✓ Android 12+ (Qualcomm/ARM) |
| Firefox 141+ | ✓ Windows |
| Firefox 145+ | ✓ macOS (ARM) |
| Safari 18.2+ | ✓ macOS, iOS, iPadOS, visionOS |
WebGPU is now baseline across major browsers.
Performance Gains
Benchmarks show 2-3x speedups over WebGL:
- Google saw 3x speedup on diffusion models
- Babylon.js Snapshot Rendering: ~10x faster with GPU Render Bundles
- More consistent frame rates (less CPU bottlenecking)
Key Differences
1. Compute Shaders
WebGL restricts GPU to graphics only. Physics, particles, post-processing must run on CPU.
WebGPU has first-class GPGPU support:
// Particle simulation - impossible in WebGL without hacks
@group(0) @binding(0) var<storage, read_write> particles: array<Particle>;
struct Particle {
position: vec3<f32>,
velocity: vec3<f32>,
}
@compute @workgroup_size(256)
fn update(@builtin(global_invocation_id) id: vec3<u32>) {
let i = id.x;
if (i >= arrayLength(&particles)) { return; }
var p = particles[i];
p.velocity.y -= 9.8 * 0.016; // Gravity
p.position += p.velocity * 0.016;
particles[i] = p;
}
Practical impact:
- Physics: 10k particles at 60fps → 100k+ particles
- Post-processing: Screen-space effects without render-to-texture hacks
- AI inference: Matrix operations on GPU (WebGPU + ONNX Runtime)
2. Multi-threaded Command Preparation
WebGL: Single thread. Every draw call, state change, resource upload is sequential. CPU often bottlenecks at 40% GPU usage.
WebGPU: Prepare render commands across multiple threads. Keep the GPU fed.
3. Reduced CPU Overhead
WebGL’s state machine model requires many API calls per draw. WebGPU uses:
- Render Bundles: Pre-record draw commands, replay cheaply
- Bind Groups: Batch resource bindings
- Pipeline State Objects: Pre-validate GPU state
WebGL state machine (many API calls):
// Every frame, for every object:
gl.useProgram(program);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(0);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(textureLocation, 0);
gl.uniformMatrix4fv(mvpLocation, false, mvpMatrix);
gl.drawArrays(gl.TRIANGLES, 0, vertexCount);
// State persists — must reset for next draw
WebGPU bind groups (batched):
// Once at setup:
const bindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{ binding: 0, resource: { buffer: uniformBuffer } },
{ binding: 1, resource: texture.createView() },
{ binding: 2, resource: sampler },
]
});
// Every frame:
passEncoder.setPipeline(pipeline);
passEncoder.setBindGroup(0, bindGroup);
passEncoder.setVertexBuffer(0, vertexBuffer);
passEncoder.draw(vertexCount);
// State doesn't leak — explicit and isolated
Performance difference:
- WebGL: ~500 draw calls/frame before CPU bottleneck
- WebGPU: ~10,000+ draw calls/frame, GPU-bound
4. Power Efficiency
Less CPU overhead = less heat = more consistent mobile performance.
A game that drops from 60→30 FPS after 5 minutes on WebGL might hold 60 FPS on WebGPU.
Shading Language
WebGL uses GLSL. WebGPU uses WGSL (WebGPU Shading Language):
GLSL (WebGL)
#version 300 es
precision highp float;
in vec2 vTexCoord;
uniform sampler2D uTexture;
uniform float uTime;
out vec4 fragColor;
void main() {
vec2 uv = vTexCoord + 0.01 * sin(uTime + vTexCoord.y * 10.0);
fragColor = texture(uTexture, uv);
}
WGSL (WebGPU)
struct Uniforms {
time: f32,
}
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
@group(0) @binding(1) var myTexture: texture_2d<f32>;
@group(0) @binding(2) var mySampler: sampler;
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) texCoord: vec2<f32>,
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
let uv = in.texCoord + 0.01 * sin(uniforms.time + in.texCoord.y * 10.0);
return textureSample(myTexture, mySampler, uv);
}
WGSL Advantages
- Explicit resource binding — No implicit global state
- Strong typing —
vec4<f32>not justvec4 - Struct-based I/O — Clear data flow
- Compute shader support — Same language for graphics and compute
- Better tooling — Static analysis, IDE support
WGSL Limitations
- Verbose compared to GLSL
- No GLSL-style swizzle chains (
rgbaonly, notxyzw) - Different texture sampling syntax
- Still evolving (spec not frozen)
Migration Strategy
If supporting both WebGL and WebGPU (like Figma):
- Abstract rendering backend
- Maintain shaders in both GLSL and WGSL (or use transpiler)
- Feature-detect and fall back gracefully
- Leverage compute shaders only on WebGPU path
When to Use What
| Use Case | Recommendation |
|---|---|
| New project (2025+) | WebGPU first, WebGL fallback |
| Existing WebGL app | Migrate incrementally |
| Must support old browsers | WebGL only |
| Compute-heavy (AI, sim) | WebGPU required |
The Future
WebGL is frozen — no new features. WebGPU will continue evolving.
Browser vendors have signaled WebGPU as the long-term standard.
Sources:
- MDN: WebGPU API
- web.dev: WebGPU supported
- Three.js Roadmap: WebGL vs WebGPU
- W3C WGSL Specification
- WebGPU 2025 Developer Guide
Related: building figma today, vello gpu vector graphics, webgpu future roadmap