A canvas accepts only bgra8unorm, rgba8unorm and rgba16float; configure() threw a TypeError here for bgra8unorm-srgb and r8unorm. navigator.gpu.getPreferredCanvasFormat() returns the 8-bit format the system displays best (bgra8unorm here). Pipelines must name the canvas's format (Validation Mistakes), so query it once and pass it everywhere.
For gamma-correct output, configure the plain format, list its -srgb twin in viewFormats, and render through an sRGB view. This page clears to 0.5 grey through each view and reads the bytes back:
<canvas id="swatch" width="4" height="4"></canvas>
<script type="module">
const device = await (await navigator.gpu.requestAdapter()).requestDevice();
const context = swatch.getContext('webgpu');
context.configure({ device, format: 'bgra8unorm', viewFormats: ['bgra8unorm-srgb'],
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC });
for (const format of ['bgra8unorm', 'bgra8unorm-srgb']) {
const texture = context.getCurrentTexture(), encoder = device.createCommandEncoder();
encoder.beginRenderPass({ colorAttachments: [{ view: texture.createView({ format }),
clearValue: [0.5, 0.5, 0.5, 1], loadOp: 'clear', storeOp: 'store' }] }).end();
const read = device.createBuffer({ size: 1024, usage: 9 }); // MAP_READ | COPY_DST
encoder.copyTextureToBuffer({ texture }, { buffer: read, bytesPerRow: 256 }, [4, 4]);
device.queue.submit([encoder.finish()]), await read.mapAsync(GPUMapMode.READ);
console.log(`${format}: ${new Uint8Array(read.getMappedRange(), 0, 4)}`);
await new Promise(requestAnimationFrame); // next frame, next texture
}
</script>bgra8unorm: 127,127,127,255 bgra8unorm-srgb: 188,188,188,255
The sRGB view encodes 0.5 as 188, which the display decodes back to half the light. Use it when shaders work in linear light (sRGB), and the plain view for colors already in sRGB, such as BookNest's blue.