@vectorvesper/motionWebGPU · Device health

watchGPUDevice

A WebGPU device can be taken away for the same reasons a WebGL context can, and it tells you in a completely different way. WebGL fires an event. WebGPU resolves a promise it handed you when the device was created. Code written for the event never hears the promise, so the canvas stops drawing with nothing thrown and a clean console.

watchGPUDevice tells the runtime when a WebGPU device dies. Hand it a device, and it hands back a function that stops watching.

A loss increments the same generation counter a lost WebGL context does. A scene gate keyed on that counter rebuilds against a device you request fresh, so a recovery path written for WebGL works here with nothing changed. The counter never cared which graphics API died.

Through React Three Fiber you never call this yourself. Spread useRenderQuality's props and the adapter finds the device and watches it for you.

Added in 4.1.0. No WebGPU type packages required: the parameter covers the one field this reads, so a real device satisfies it either way.

◆What your browser exposes

LIVE PROBE
Demo loads on scroll

Programmatic device destruction via device.destroy() is intentionally ignored by the runtime to prevent false-positive recovery loops during component unmounting. Testing hardware-level device loss requires driver crash simulation or GPU memory eviction.

Quick start

Request a device, hand it over, stop watching when you tear it down.

Both orders are safe, because a destroyed device is ignored on its reason alone. Stopping first just means the resolution arrives with nothing listening.

Why a destroyed device is ignored

Differentiating between programmatic device destruction and unexpected hardware failure.

In WebGPU, GPUDevice.lost resolves with a GPUDeviceLostInfo structure containing a reason property. When application code intentionally disposes of a device by invoking device.destroy(), reason is set to "destroyed". If the GPU encounters an unrecoverable driver crash, device reset, or memory eviction, reason resolves to "unknown".

Failing to distinguish between intentional teardown and unexpected failure creates an infinite reconstruction loop: disposing of a device during component unmount triggers a context-loss event, which increments the scene generation counter, mounts a replacement, unmounts previous resources, and fires additional loss events.

The runtime coalesces simultaneous device loss events across multiple canvases within a 250ms debounce window. A single GPU reset affecting multiple active contexts increments the generation counter once, avoiding duplicate re-render cycles.

Calling stop() flags the observer as inactive. Because standard Promises cannot be cancelled once attached, the internal listener continues execution upon resolution but suppresses telemetry and recovery dispatches.

API Reference

Syntax

Registers a device as healthy upon initialization and monitors GPUDevice.lost for unexpected hardware failures.

Parameters

Parameters.fieldTypeDefaultDescription
deviceGPUDeviceLike(none)Any WebGPU device instance or wrapper exposing a lost Promise adhering to the GPUDevice specification.

Return Value

StopWatchingDevice

Deactivates reporting. Safe to invoke idempotently during unmount or after device loss.

Loss reasons

Evaluation and dispatch policy for GPUDeviceLostInfo reason values.

Loss reasons.fieldMeaningRuntime Action
"destroyed"Intentional disposal via device.destroy().Ignored; teardown completes normally.
"unknown"Driver reset, GPU hang, or OS resource reclaim.Loss reported; scene generation increments.
undefinedImplementation resolved without reason metadata.Treated as unexpected loss; recovery initiated.

Lifecycle & Invariant Guarantees

  • →Teardown vs. Failure Discrimination: Intentional device destruction during component unmount never triggers context recovery rebuilds.
  • →Coalesced Driver Reset Handling: Loss events occurring within 250ms are coalesced into a single recovery cycle, preventing multi-canvas cascade rebuilds.
  • →Holding a device reports health: Receiving a device ends a recovery. The replacement announcing itself is the signal that the scene is drawable again.
  • →A non-conformant promise cannot break the page: The spec says the promise resolves. An implementation that rejects instead reports nothing rather than surfacing an unhandled rejection.

◆Production Examples

Battle-tested production patterns ready to copy directly into your codebase.

The generation is the recovery. A new key means a device requested fresh.

When NOT to use this

✕
The scene renders through React Three Fiber.
→ instead useRenderQuality. It finds the device and watches it, and attaches the WebGL listener too.
✕
You hold a raw WebGL context rather than a WebGPU device.
→ instead A webglcontextlost listener that calls preventDefault, reporting to getRendererHealth().
✕
You only want to know whether the browser has WebGPU at all.
→ instead The webgpu flag on AdaptiveQuality's device signals. It needs no adapter.
✕
The page draws with Canvas 2D or CSS.
→ instead Nothing. Neither can lose a device this way.

Rules & Gotchas

  • →Stop watching in the cleanup that destroys the device: A watcher outliving the component that owns the device is how a teardown ends up reported as a failure.
  • →Request a new device on a new generation: A lost device cannot be revived. Recovery means requesting another, which is what keying the subtree on generation makes React do.
  • →Do not await the lost promise in a render path: It resolves only when the device dies, which on a healthy machine is never.
  • →Do not read navigator.gpu as proof of a working device: A blocklisted driver exposes the API and then refuses the adapter, so the request can still fail.

Related Motion Components