# WebGPU API

> requestAdapter() and requestDevice() expose the GPU for rendering and compute. Options, every rejection the spec defines, device loss, and the WebGL2 fallback.

WebGPU gives a page the system GPU for rendering and general-purpose compute through `navigator.gpu`: `requestAdapter()` picks a physical or software adapter, `requestDevice()` turns it into the `GPUDevice` that creates buffers, textures, pipelines, and command encoders, and shaders are written in WGSL rather than WebGL's GLSL. The whole API is `[SecureContext]` and is also exposed on `WorkerNavigator`, so a dedicated worker can own the device.

Support is uneven by operating system rather than by browser version alone (BCD `api.GPU`). Chrome 113 shipped it on ChromeOS, macOS, and Windows; Chrome 144 added Linux for Intel Gen12 and newer GPUs; Chrome 121 covers Android. Firefox 141 ships on Windows only, with Apple silicon macOS following in Firefox 145 (macOS Tahoe) and 147 (older macOS), no Intel Mac ([bug 2004105](https://bugzil.la/2004105)), no Linux ([bug 2006676](https://bugzil.la/2006676)), and no service-worker contexts ([bug 1942431](https://bugzil.la/1942431)); Firefox for Android has no implementation. Safari 26 ships it on macOS, iOS, iPadOS, and visionOS.

## Syntax

```js
navigator.gpu.requestAdapter()
navigator.gpu.requestAdapter(options)

adapter.requestDevice()
adapter.requestDevice(descriptor)

navigator.gpu.getPreferredCanvasFormat()
canvas.getContext("webgpu")
```

`requestAdapter()` returns `Promise<GPUAdapter?>`: it resolves with `null`, rather than rejecting, when the browser has no adapter that meets `options`. `requestDevice()` returns `Promise<GPUDevice>` and consumes the adapter; a second call on the same `GPUAdapter` rejects. `getPreferredCanvasFormat()` returns the `GPUTextureFormat` (`"bgra8unorm"` or `"rgba8unorm"`) the canvas context should be configured with on this system.

## Parameters

`requestAdapter()` takes an optional `GPURequestAdapterOptions` dictionary; `requestDevice()` takes an optional `GPUDeviceDescriptor`. Both have defaults for every member, so the bare calls are valid.

| Member | Type | Required | Description |
|---|---|---|---|
| `options.featureLevel` | `DOMString` | No | `"core"` (default) or `"compatibility"`. Requests an adapter that supports at least that level's limits and features; an unsupported level resolves with `null`. A browser that does not implement compatibility mode treats the request as `"core"`. |
| `options.powerPreference` | `GPUPowerPreference` | No | `"low-power"` or `"high-performance"`; a hint only. Chrome on Windows ignores it and reuses the adapter it already allocated for the browser ([crbug.com/369219127](https://crbug.com/369219127)). |
| `options.forceFallbackAdapter` | `boolean` | No | Default `false`. When `true`, only a software (fallback) adapter may be returned; `null` if none exists. |
| `options.xrCompatible` | `boolean` | No | Default `false`. Requests an adapter that can present to a WebXR session. |
| `descriptor.requiredFeatures` | `sequence<GPUFeatureName>` | No | Default `[]`. Feature names the device must expose; exactly this set is enabled on the device, so an optional feature you forget to list is unavailable even when the adapter has it. |
| `descriptor.requiredLimits` | `record<DOMString, GPUSize64 or undefined>` | No | Default `{}`. Limits the device must meet; each key must name a supported limit and the value may not exceed the adapter's own. Validation on the device uses these exact limits, not the adapter's. |
| `descriptor.defaultQueue` | `GPUQueueDescriptor` | No | Default `{}`. Only carries a `label` for `device.queue`. |
| `descriptor.label` | `USVString` | No | Inherited from `GPUObjectDescriptorBase`; shown in error messages and DevTools. |

## Exceptions

`requestAdapter()` has no rejection path for a missing GPU: an absent or unsuitable adapter resolves with `null`. `requestDevice()` rejects with two names, in the order the specification checks them (`[GPUAdapter.requestDevice()](https://www.w3.org/TR/webgpu/#dom-gpuadapter-requestdevice)` (w3.org)).

| Exception | Condition |
|---|---|
| `TypeError` | `requiredFeatures` is not a subset of `adapter.features`. The spec deliberately uses the same error whether the browser does not know the name or this adapter lacks it. |
| `OperationError` | The adapter is already consumed by an earlier `requestDevice()`; or a `requiredLimits` key is not a supported limit; or a value is better than the adapter's limit; or an alignment limit is not a power of two below 2^32. |

Two failure modes are not rejections. If the adapter has expired or the browser cannot fulfil the request, `requestDevice()` still resolves, but with a device that is already lost: `device.lost` resolves with a `GPUDeviceLostInfo` whose `reason` is `"unknown"` before the `requestDevice()` promise itself resolves. Validation, out-of-memory, and internal errors during use arrive as `GPUValidationError`, `GPUOutOfMemoryError`, and `GPUInternalError` objects through `device.popErrorScope()` or the `uncapturederror` event, not as thrown exceptions.

:::observed
On a platform where Chrome's `WebGPUService` feature is disabled, the first `navigator.gpu.requestAdapter()` call prints an info-level console message, `WebGPU is experimental on this platform. See https://github.com/gpuweb/gpuweb/wiki/Implementation-Status#implementation-status`, before resolving. Passing a feature the adapter lacks, for example `requestDevice({ requiredFeatures: ["texture-compression-astc"] })` on a desktop adapter without ASTC, rejects with `TypeError: Unsupported feature: texture-compression-astc`. Both strings are in Chromium's [`gpu.cc`](https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/modules/webgpu/gpu.cc) and [`gpu_adapter.cc`](https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc) (chromium.googlesource.com).
:::

## Examples

Every example checks for `navigator.gpu` first and handles the `null` adapter, because both happen on shipping browsers: Firefox for Android has no `navigator.gpu`, and Firefox on an Intel Mac has `navigator.gpu` but no adapter.

### Detecting support and falling back to WebGL2

The feature check has two stages: the property exists, and an adapter is returned. Treat both failures the same way and hand the canvas to a WebGL2 renderer, which Chrome 56, Firefox 51, and Safari 15 all provide.

```js
async function createRenderer(canvas) {
  if (!("gpu" in navigator)) {
    return createWebGL2Renderer(canvas);
  }
  const adapter = await navigator.gpu.requestAdapter();
  if (!adapter) {
    return createWebGL2Renderer(canvas);
  }
  const device = await adapter.requestDevice();
  const context = canvas.getContext("webgpu");
  context.configure({ device, format: navigator.gpu.getPreferredCanvasFormat() });
  return createWebGPURenderer(device, context);
}

function createWebGL2Renderer(canvas) {
  const gl = canvas.getContext("webgl2");
  if (!gl) throw new Error("Neither WebGPU nor WebGL2 is available");
  return { kind: "webgl2", gl };
}
```

`getContext("webgpu")` on a canvas that already holds a 2D or WebGL context returns `null`, so create the WebGPU context before any fallback touches the same element.

### Requesting optional features and limits without losing the device request

Ask the adapter what it has, then request only what it can give. Listing a missing feature rejects with `TypeError`; asking for a limit above the adapter's rejects with `OperationError`, so clamp against `adapter.limits` instead of hard-coding.

```js
async function requestCapableDevice(adapter) {
  const requiredFeatures = [];
  for (const name of ["timestamp-query", "texture-compression-bc", "shader-f16"]) {
    if (adapter.features.has(name)) requiredFeatures.push(name);
  }
  const wanted = 1024 * 1024 * 1024; // 1 GiB storage buffers
  const requiredLimits = {
    maxStorageBufferBindingSize: Math.min(wanted, adapter.limits.maxStorageBufferBindingSize),
  };
  try {
    return await adapter.requestDevice({ requiredFeatures, requiredLimits });
  } catch (err) {
    if (err.name === "OperationError") {
      return adapter.requestDevice(); // adapter is still unconsumed after a rejection
    }
    throw err;
  }
}
```

A rejected `requestDevice()` does not consume the adapter, so the retry with defaults works on the same `GPUAdapter`.

### Recovering from device loss

`device.lost` is a promise that settles once per device, with `reason` `"destroyed"` after your own `device.destroy()` call or `"unknown"` for everything else (driver reset, GPU process crash, tab backgrounded on a memory-constrained phone). Every `GPUBuffer`, `GPUTexture`, and pipeline created from the lost device is invalid, so recovery starts again from `requestAdapter()`.

```js
async function initGpu(canvas, onLost) {
  const adapter = await navigator.gpu.requestAdapter();
  if (!adapter) return null;
  const device = await adapter.requestDevice();

  device.lost.then((info) => {
    console.warn(`GPU device lost (${info.reason}): ${info.message}`);
    if (info.reason !== "destroyed") onLost(); // re-run initGpu and rebuild resources
  });

  device.addEventListener("uncapturederror", (event) => {
    console.error(event.error.message);
  });
  return device;
}
```

The `uncapturederror` listener catches validation failures that no `pushErrorScope()`/`popErrorScope()` pair wrapped; without it Chrome reports them only in the DevTools console.

## See also

- [WebCodecs](/reference/capabilities/webcodecs/), whose `VideoFrame` objects can be imported as WebGPU external textures
- [Core Web Vitals for PWAs](/reference/performance/core-web-vitals/), where GPU work on the main thread shows up as INP
- [Offline strategies](/guides/offline/), for caching the WGSL and model assets a WebGPU app downloads
- [WebGPU: GPU.requestAdapter()](https://www.w3.org/TR/webgpu/#dom-gpu-requestadapter) (w3.org)
- [WebGPU: GPUAdapter.requestDevice()](https://www.w3.org/TR/webgpu/#dom-gpuadapter-requestdevice) (w3.org)
- [WebGPU implementation status](https://github.com/gpuweb/gpuweb/wiki/Implementation-Status) (github.com)
- [Chrome ships WebGPU](https://developer.chrome.com/blog/webgpu-release) (developer.chrome.com)
- [News from WWDC25: web technology coming this fall in Safari 26 beta](https://webkit.org/blog/16993/news-from-wwdc25-web-technology-coming-this-fall-in-safari-26-beta/) (webkit.org)