Off-Thread Recording

Recording Commands Off the Main Thread

WebGPU works in dedicated, shared and service workers, and a canvas transferred to a worker as an OffscreenCanvas lets the worker own the whole render loop:

A worker that owns the canvas, records the commands and drawsHTMLLive
<canvas id="shelf" width="480" height="120"></canvas>
<script type="module">
const workerCode = `onmessage = async ({ data: canvas }) => {
  const adapter = await navigator.gpu.requestAdapter();
  const device = await adapter.requestDevice();
  const context = canvas.getContext('webgpu');
  context.configure({ device, format: navigator.gpu.getPreferredCanvasFormat() });
  const encoder = device.createCommandEncoder();          // recorded entirely in the worker
  const view = context.getCurrentTexture().createView();
  encoder.beginRenderPass({ colorAttachments: [{ view, loadOp: 'clear', storeOp: 'store',
    clearValue: [0.08, 0.40, 0.75, 1] }] }).end();
  device.queue.submit([encoder.finish()]);
  await device.queue.onSubmittedWorkDone();
  postMessage(self.constructor.name + ' drew on ' + adapter.info.vendor);
};`;
const blob = new Blob([workerCode], { type: 'text/javascript' });
const worker = new Worker(URL.createObjectURL(blob));
const offscreen = shelf.transferControlToOffscreen();   // the canvas now belongs to the worker
worker.postMessage(offscreen, [offscreen]);
worker.onmessage = ({ data }) => console.log(data);
</script>

The limit today is sharing: posting a GPUDevice to a worker throws a DataCloneError (checked in Chrome 154 1 ), so each worker needs its own device, and two threads cannot record into one device in parallel the way Vulkan or Direct3D 12 command lists can. OffscreenCanvas builds the full pattern.