demo · v139
Cross-Platform Renderer
Request two WebGPU adapters — one standard, one with featureLevel: 'compatibility' — and render the same triangle to side-by-side canvases. Inspect adapter info, backend name, and the feature restrictions that compat mode imposes.
feature diff: standard vs compat
| capability | standard | compat mode |
|---|---|---|
| Storage textures in vertex shaders | yes | no — removed |
| bgra8unorm storage textures | yes (with feature) | no |
| Non-zero firstInstance in draws | yes (with feature) | no |
| Cube array textures | yes | no |
| Separate depth/stencil aspects | yes | no |
| MSAA render-to-texture | yes | limited — no auto-resolve |
| Texture format reinterpretation (view formats) | yes | no |
| provoking vertex convention | last vertex | undefined (driver decides) |
platform coverage
Compat mode specifically targets hardware where Vulkan or Metal are unavailable. Standard mode already covers most modern devices.
Windows
D3D12 / Vulkan
standard ✓
compat ✓
macOS
Metal
standard ✓
compat ✓
Android (new)
Vulkan
standard ✓
compat ✓
Android (old)
OpenGL ES 3.1
standard ✗
compat ✓
Linux
Vulkan / GL
standard ✓
compat ✓
ChromeOS (old)
OpenGL ES
standard ✗
compat ✓
who should use compat mode?
▸ Does your app need to run on Android devices older than 2019?
yes →
▸ Does your app use storage textures in vertex shaders, cube arrays, or MSAA auto-resolve?
yes → Refactor those features; then enable compat mode for the broader device reach.
no → Use compat mode. You get OpenGL ES 3.1 coverage with zero code changes.
no →
▸ Do you need maximum GPU feature access (ray tracing, advanced formats, etc.)?
yes → Use standard mode only.
no → Either works. Compat is a safe default for forward compatibility.
the code
// Standard adapter
const stdAdapter = await navigator.gpu.requestAdapter();
// Compatibility mode adapter (Chrome 139)
const cmpAdapter = await navigator.gpu.requestAdapter({
featureLevel: 'compatibility',
});
const stdDevice = await stdAdapter.requestDevice();
const cmpDevice = await cmpAdapter.requestDevice();
// Check which mode you got
console.log(stdDevice.features.has('core-features-and-limits')); // true for core
console.log(cmpDevice.features.has('core-features-and-limits')); // false for compat subset
// Render normally — same pipeline code works on both
function render(device, canvas) {
const ctx = canvas.getContext('webgpu');
ctx.configure({ device, format: navigator.gpu.getPreferredCanvasFormat() });
// … create pipeline, draw …
}