WebGPU Core Architecture
Initialize WebGPU correctly through the navigator.gpu entry point and the adapter, device, queue model, and reason about the 4-phase application runtime. Baseline: WebGPU 1.0-stable (Chrome 113+, Safari 26+, Firefox 141+).
Quick Reference
| Type / Member | Kind | Purpose |
|---|---|---|
navigator.gpu |
GPU |
Entry point. undefined on insecure origins. |
navigator.gpu.requestAdapter(options?) |
method | Returns Promise<GPUAdapter | null>. Null is a valid resolution. |
navigator.gpu.getPreferredCanvasFormat() |
method | Returns "bgra8unorm" or "rgba8unorm". |
GPUAdapter |
interface | Physical implementation. Has features, limits, info. |
GPUAdapter.info |
GPUAdapterInfo |
vendor, architecture, device, description. |
GPUAdapter.requestDevice(descriptor?) |
method | Returns Promise<GPUDevice>. Resolves even on failure. |
GPUDevice |
interface | Logical connection. Root owner of all child objects. |
GPUDevice.queue |
property | Read-only GPUQueue. NEVER call it. |
GPUDevice.lost |
property | Read-only Promise<GPUDeviceLostInfo>. |
GPUDevice.destroy() |
method | Releases the device and all child objects. |
GPUQueue.submit(buffers) |
method | Schedules command buffers for execution. |
WebGPU is a layered API. The entry point exposes the GPU interface. WebGPU ALWAYS requires a secure context (HTTPS or localhost). On insecure origins navigator.gpu is undefined.
The three-step async chain:
const adapter = await navigator.gpu.requestAdapter(options?); // Promise<GPUAdapter | null>
const device = await adapter.requestDevice(descriptor?); // Promise<GPUDevice>
const queue = device.queue; // GPUQueue property
Decision Tree
Is navigator.gpu defined?
├─ NO → page is not on a secure context, OR the browser lacks WebGPU.
│ ALWAYS show a fallback message. STOP. Do not call requestAdapter.
└─ YES → call navigator.gpu.requestAdapter(options)
Which powerPreference?
├─ Real-time 3D, games, heavy compute → "high-performance"
├─ UI effects, battery-sensitive, mobile → "low-power"
└─ Unsure → omit powerPreference entirely (the UA chooses)
Need to test the software/portability path?
├─ YES → set forceFallbackAdapter: true to request a fallback adapter
└─ NO → omit forceFallbackAdapter
Did requestAdapter resolve to null?
├─ YES → no compatible GPU. ALWAYS show a fallback message. STOP.
└─ NO → call adapter.requestDevice(descriptor)
After requestDevice resolves:
├─ ALWAYS register device.lost.then(...) BEFORE creating resources
└─ Access the queue as device.queue (property), then create resources
Core Patterns
Pattern 1: ALWAYS guard navigator.gpu before any WebGPU call
navigator.gpu is undefined on insecure origins and in browsers without WebGPU. Accessing .requestAdapter on undefined throws a TypeError.
if (!navigator.gpu) {
throw new Error("WebGPU is not available. Use HTTPS or localhost, and a supporting browser.");
}
Pattern 2: ALWAYS null-check the adapter
requestAdapter() returns Promise<GPUAdapter | null>. null is a valid resolution, NOT a rejection. It means no compatible GPU was found.
const adapter = await navigator.gpu.requestAdapter({ powerPreference: "high-performance" });
if (!adapter) {
throw new Error("No WebGPU adapter found.");
}
Pattern 3: ALWAYS register device.lost before creating resources
requestDevice() never throws for runtime failures. It can resolve to a GPUDevice that has already been lost. Register the lost handler first so loss is observed even if it happens during initialization.
const device = await adapter.requestDevice();
device.lost.then((info) => {
console.error(`Device lost: ${info.reason} : ${info.message}`);
// See webgpu-errors-device-loss for the full recovery pattern.
});
Pattern 4: ALWAYS access device.queue as a property, NEVER as a call
device.queue is a read-only GPUQueue property. Calling device.queue() throws TypeError: device.queue is not a function.
const queue = device.queue; // correct
queue.submit([commandBuffer]); // correct
// const queue = device.queue(); // WRONG, throws TypeError
Pattern 5: Request only the features and limits you need
requestDevice() accepts a GPUDeviceDescriptor with requiredFeatures and requiredLimits. ALWAYS feature-detect against adapter.features first. Requesting an absent feature makes requestDevice() fail.
const device = await adapter.requestDevice({
label: "main-device",
requiredFeatures: adapter.features.has("shader-f16") ? ["shader-f16"] : [],
});
Pattern 6: The device is the root owner
The adapter is the physical implementation. The device is a logical connection that abstracts the implementation so the owner acts as if it is the sole user of the adapter. Every buffer, texture, pipeline, and bind group is created from the device and is owned by it. When the device is lost, all child objects are freed together. ALWAYS recreate every resource against a new device after recovery.
Common Anti-Patterns
Not null-checking the adapter.
requestAdapter()can resolve tonull. Code that doesawait navigator.gpu.requestAdapter()thenadapter.requestDevice()throwsTypeError: Cannot read properties of nullon machines with no compatible GPU.Calling
device.queue()as a method.device.queueis a read-only property.device.queue()throwsTypeError: device.queue is not a function.Assuming
requestDevice()rejects on failure. It does not. It resolves to aGPUDevicethat may already be lost. Code that omits adevice.losthandler silently produces a dead device.
See references/anti-patterns.md for the full list with WHY-it-fails explanations, including reusing a stale adapter after device loss.
Critical Warnings
- NEVER call
navigator.gpu.requestAdapterwithout first checkingnavigator.gpuis defined. It isundefinedon insecure origins. - NEVER assume
requestAdapter()rejects when no GPU is present. It resolves tonull. ALWAYS null-check. - NEVER call
device.queueas a function. It is a read-onlyGPUQueueproperty. - NEVER assume
requestDevice()rejects on runtime failure. It can resolve to an already-lost device. ALWAYS registerdevice.lost. - NEVER reuse a stale
GPUAdapterafter a device loss. The adapter itself can become invalid. ALWAYS re-request the adapter. Seewebgpu-errors-device-loss. - NEVER request a feature or limit without checking
adapter.featuresandadapter.limitsfirst. Seewebgpu-core-limits-features. - NEVER hardcode the canvas format. Use
navigator.gpu.getPreferredCanvasFormat(). Seewebgpu-core-cross-browser.
Reference Files
references/methods.md: complete API signatures forGPU,GPUAdapter,GPUDevice,GPUQueue,GPUAdapterInfo,requestAdapter, andrequestDevice.references/examples.md: verified WebGPU initialization code, from minimal setup to feature negotiation and worker contexts.references/anti-patterns.md: initialization mistakes with WHY-it-fails explanations.
Related skills:
webgpu-core-limits-features: negotiatingrequiredFeaturesandrequiredLimits.webgpu-core-cross-browser: browser differences, secure context, preferred canvas format.webgpu-errors-device-loss: the full device-loss recovery pattern.