requestAdapter()

Requesting an Adapter with requestAdapter()

navigator.gpu.requestAdapter(options) resolves to a GPUAdapter, or to null when no suitable GPU exists (a blocklisted driver, WebGPU disabled, or unmet options). It does not reject for those reasons, so always check:

The minimal adapter request, with both failure pathsJavaScript
if (!('gpu' in navigator)) throw new Error('WebGPU is not supported in this browser');
const adapter = await navigator.gpu.requestAdapter();   // featureLevel: 'core' by default
if (!adapter) throw new Error('No adapter: the GPU is blocklisted, disabled or missing');

The options are powerPreference, forceFallbackAdapter (Power Preference), featureLevel ('core', or 'compatibility' for OpenGL ES-class hardware) and xrCompatible for WebXR. The adapter itself is small: a for...in loop over it in Chrome 154 1 lists just features, limits, info and requestDevice. Getting an adapter and a device took 240 to 350 ms after page load in the Encoders and Queues runs, so do it once at startup.

Requesting an adapter with both failure paths checked, then charting a few of its limitsHTMLLive
<!doctype html>
<style>
  body { margin: 0; background: #f7f4ee; font: 14px system-ui, sans-serif; }
  .stage { position: relative; width: 100%; max-width: 600px; }
  .stage canvas { display: block; width: 100%; }
  .stage canvas + canvas { position: absolute; inset: 0; pointer-events: none; }
</style>
<div class="stage">
  <canvas id="view" width="600" height="380"></canvas>
  <canvas id="labels" width="600" height="380"></canvas>
</div>
<script>
const canvas = document.getElementById('view');
const ink = document.getElementById('labels').getContext('2d');

function showMessage(text) {                     // 2D fallback when WebGPU is missing
  const ctx = canvas.getContext('2d');
  ctx.fillStyle = '#fbeaea'; ctx.fillRect(0, 0, canvas.width, canvas.height);
  ctx.fillStyle = '#8a2b2b'; ctx.font = '18px system-ui, sans-serif'; ctx.textAlign = 'center';
  ctx.fillText(text, canvas.width / 2, canvas.height / 2);
}
function label(text, x, y, size = 12, color = '#2b2b2b', align = 'left', weight = '') {
  ink.font = `${weight} ${size}px system-ui, sans-serif`; ink.fillStyle = color; ink.textAlign = align;
  ink.fillText(text, x, y);
}

const shown = ['maxTextureDimension2D', 'maxTextureArrayLayers', 'maxBindGroups', 'maxVertexBuffers',
  'maxUniformBufferBindingSize', 'maxStorageBufferBindingSize', 'maxBufferSize',
  'maxComputeWorkgroupStorageSize', 'maxComputeInvocationsPerWorkgroup', 'maxComputeWorkgroupsPerDimension'];

async function main() {
  // The two failure paths: no API at all, or an API that finds no suitable GPU.
  if (!('gpu' in navigator)) return showMessage('WebGPU is not available in this browser');
  const t0 = performance.now();
  const adapter = await navigator.gpu.requestAdapter();   // featureLevel: 'core' by default
  if (!adapter) return showMessage('No adapter: the GPU is blocklisted, disabled or missing');
  const device = await adapter.requestDevice();
  const elapsed = performance.now() - t0;
  const context = canvas.getContext('webgpu');
  const format = navigator.gpu.getPreferredCanvasFormat();
  context.configure({ device, format });

  const members = [];
  for (const key in adapter) members.push(key);      // a small object: features, limits, info...
  label(`Adapter and device in ${elapsed.toFixed(0)} ms. Members: ${members.join(', ')}`, 14, 22, 12, '#222');
  label(`${adapter.features.size} optional features, e.g. ${[...adapter.features].slice(0, 3).join(', ')}`, 14, 40, 12, '#444');
  label('adapter.limits (log scale; the marker shows the default device limit)', 14, 64, 12, '#1f4f8a', 'left', 'bold');

  const boxes = [];                                  // x, y, w, h, r, g, b, corner radius
  const scale = v => 16 * Math.log2(v + 1);          // log2 bars: 16 px per doubling
  shown.forEach((name, i) => {
    const y = 76 + i * 29, value = adapter.limits[name], base = device.limits[name];
    boxes.push([250, y, Math.min(scale(value), 336), 20, 0.20, 0.50, 0.80, 5]);
    boxes.push([250 + Math.min(scale(base), 336) - 1, y - 3, 3, 26, 0.85, 0.45, 0.10, 1]);
    label(name, 244, y + 15, 11.5, '#333', 'right');
    label(value.toLocaleString('en-US'), 256, y + 15, 11, '#fff', 'left', 'bold');
  });

  const module = device.createShaderModule({ code: `
    struct Box { rect: vec4f, style: vec4f }          // style: r, g, b, corner radius
    @group(0) @binding(0) var<storage> boxes: array<Box>;
    struct Out { @builtin(position) pos: vec4f, @location(0) local: vec2f,
                 @location(1) @interpolate(flat) i: u32 }
    @vertex fn vs(@builtin(vertex_index) v: u32, @builtin(instance_index) i: u32) -> Out {
      let corner = vec2f(f32(v & 1), f32(v >> 1));   // triangle-strip corners
      let r = boxes[i].rect;
      let px = r.xy + corner * r.zw;
      return Out(vec4f(px.x / 300 - 1, 1 - px.y / 190, 0, 1), corner * r.zw, i);
    }
    @fragment fn fs(in: Out) -> @location(0) vec4f {
      let b = boxes[in.i];
      let half = b.rect.zw / 2;
      let q = abs(in.local - half) - half + b.style.w; // signed distance to a rounded box
      let d = length(max(q, vec2f(0))) + min(max(q.x, q.y), 0) - b.style.w;
      let a = clamp(0.5 - d, 0, 1);
      return vec4f(b.style.rgb * a, a);               // premultiplied alpha
    }` });
  const blend = { srcFactor: 'one', dstFactor: 'one-minus-src-alpha' };
  const pipeline = device.createRenderPipeline({ layout: 'auto',
    vertex: { module }, primitive: { topology: 'triangle-strip' },
    fragment: { module, targets: [{ format, blend: { color: blend, alpha: blend } }] } });
  const data = new Float32Array(boxes.flat());
  const buffer = device.createBuffer({ size: data.byteLength, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST });
  device.queue.writeBuffer(buffer, 0, data);
  const group = device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [{ binding: 0, resource: { buffer } }] });

  const encoder = device.createCommandEncoder();
  const pass = encoder.beginRenderPass({ colorAttachments: [{ view: context.getCurrentTexture().createView(),
    clearValue: [0.97, 0.96, 0.93, 1], loadOp: 'clear', storeOp: 'store' }] });
  pass.setPipeline(pipeline);
  pass.setBindGroup(0, group);
  pass.draw(4, boxes.length);
  pass.end();
  device.queue.submit([encoder.finish()]);
}
main();
</script>