Skip to content

Instantly share code, notes, and snippets.

@greggman
Last active April 23, 2026 19:06
Show Gist options
  • Select an option

  • Save greggman/6f92b33b4835588a50c1afee6e0c15c3 to your computer and use it in GitHub Desktop.

Select an option

Save greggman/6f92b33b4835588a50c1afee6e0c15c3 to your computer and use it in GitHub Desktop.
WebGPU: rAF loop with getCurrentTexture and no GPUTextureView

WebGPU: rAF loop with getCurrentTexture and no GPUTextureView

view on jsgist

html, body {
margin: 0; /* remove the default margin */
height: 100%; /* make the html,body fill the page */
}
canvas {
display: block; /* make the canvas act like a block */
width: 100%; /* make the canvas fill its container */
height: 100%;
}
<canvas></canvas>
async function main() {
const adapter = await navigator.gpu?.requestAdapter();
const device = await adapter?.requestDevice();
if (!device) {
fail('need a browser that supports WebGPU');
return;
}
// Get a WebGPU context from the canvas and configure it
const canvas = document.querySelector('canvas');
const context = canvas.getContext('webgpu');
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
context.configure({
device,
format: presentationFormat,
});
const renderPassDescriptor = {
label: 'our basic canvas renderPass',
colorAttachments: [
{
// view: <- to be filled out when we render
clearValue: [0.3, 0.3, 0.3, 1],
loadOp: 'clear',
storeOp: 'store',
},
],
};
let renderTarget;
function render() {
const canvasTexture = context.getCurrentTexture();
const { width, height } = canvasTexture;
if (renderTarget?.width !== width || renderTarget?.height !== height) {
renderTarget?.destroy();
renderTarget = device.createTexture({
size: [width, height],
format: presentationFormat,
usage: GPUTextureUsage.RENDER_ATTACHMENT,
sampleCount: 4,
});
}
renderPassDescriptor.colorAttachments[0].view = renderTarget;
renderPassDescriptor.colorAttachments[0].resolveTarget = canvasTexture;
renderPassDescriptor.colorAttachments[0].clearValue[0] = performance.now() / 1000 % 1;
const encoder = device.createCommandEncoder({ label: 'our encoder' });
const pass = encoder.beginRenderPass(renderPassDescriptor);
pass.end();
const commandBuffer = encoder.finish();
device.queue.submit([commandBuffer]);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
const observer = new ResizeObserver(entries => {
for (const entry of entries) {
const canvas = entry.target;
const width = entry.contentBoxSize[0].inlineSize;
const height = entry.contentBoxSize[0].blockSize;
canvas.width = Math.max(1, Math.min(width, device.limits.maxTextureDimension2D));
canvas.height = Math.max(1, Math.min(height, device.limits.maxTextureDimension2D));
}
});
observer.observe(canvas);
}
function fail(msg) {
// eslint-disable-next-line no-alert
alert(msg);
}
main();
{"name":"WebGPU: rAF loop with getCurrentTexture and no GPUTextureView","settings":{},"filenames":["index.html","index.css","index.js"]}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment