Skip to content

Instantly share code, notes, and snippets.

@greggman
Created August 28, 2025 05:15
Show Gist options
  • Save greggman/5456ce22689e97886b2946c0b37ea4f9 to your computer and use it in GitHub Desktop.
Save greggman/5456ce22689e97886b2946c0b37ea4f9 to your computer and use it in GitHub Desktop.
HDR draw (freeze)
:root {
color-scheme: light dark;
}
html, body {
margin: 0;
height: 100%;
}
canvas {
width: 100%;
height: 100%;
display: block;
}
#ui {
position: absolute;
right: 0;
top: 0;
}
label {
display: flex;
align-items: center;
}
#rgb {
display: flex;
flex-direction: column;
}
<canvas></canvas>
<div id="ui">
<div id="rgb">
<label>r: <input type="range" min="0" max="10" id="r" value="10" step="0.01"></label>
<label>g: <input type="range" min="0" max="10" id="g" value="10" step="0.01"></label>
<label>b: <input type="range" min="0" max="10" id="b" value="10" step="0.01"></label>
</div>
</div>
import {
mat4,
} from 'https://wgpu-matrix.org/dist/3.x/wgpu-matrix.module.js';
const adapter = await navigator.gpu?.requestAdapter();
const device = await adapter?.requestDevice();
device.addEventListener('uncapturederror', e => console.error(e.error.message));
const canvas = document.querySelector('canvas');
const context = canvas.getContext('webgpu');
const presentationFormat = 'rgba16float';
context.configure({
device,
format: presentationFormat,
toneMapping: { mode: 'extended' },
});
const module = device.createShaderModule({
code: `
struct Uniforms {
matrix: mat4x4f;
color: vec4f;
hardness: f32;
radius: f32;
};
struct VSOut {
@builtin(position) pos: vec4f;
@location(0) uv: vec2f;
};
@group(0) @binding(0) var<uniform> u: Uniforms;
@vertex vsDraw(@builtin(vertex_index) i: u32) -> VSOut {
let pos = array(
vec2f(0, 0),
vec2f(1, 0),
vec2f(0, 1),
vec2f(1, 1),
);
let p = pos[vertex_index]
return VSOut(
u.matrix * vec4f(p, 0, 1),
p,
);
}
@fragment fsDraw(v: VSOut) -> @location(0) vec4f {
let uv = v.uv * 2 - 1;
let a = smoothstep(radius, radius * hardness, distance(uv));
return u.color * a;
}
`,
});
const drawPipeline = device.createRenderPipeline({
layout: 'auto',
vertex: { module },
fragment: { module, targets: [{ format: 'rgba16unorm' }] },
primitive: {
topology: 'triangle-strip',
},
});
console.log('done'); /*
const uniformBuffer = device.createBuffer({
size: (16 + 4 + 1 + 1 + 2) * 4,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
const bindGroup = device.createBindGroup({
layout: draw.getBindGroupLayout(0),
entries: [
{ binding: 0, resource: { buffer: uniformBuffer } },
],
});
const settings = {
color: [1, 0.5, 0.2, 1],
hardness: 0.5,
radius: 1.0,
};
function draw(x, y) {
const canvasTexture = context.getCurrentTexture();
const { width, height } = canvasTexture;
const matrix = m4.ortho(0, 0, width, height, 0, 1);
mat4.translate(matrix, [x * width, y * height, 0], matrix);
mat4.scale(matrix, [40, 40, 1], matrix);
mat4.translate(matrix, [-0.5, -0.5, 0], matrix);
device.queue.writeBuffer(uniformBuffer, 0, new Float32Array([
...matrix,
...settings.color,
settings.hardness,
settings.radius,
]))
const encoder = device.createCommandEncoder();
const pass = encoder.beginRenderPass({
colorAttachments: [
{
view: canvasTexture.createView(),
loadOp: 'clear',
storeOp: 'store',
clearValue: [1, 1, performance.now() % 1000 / 1000, 1],
},
],
});
pass.setPipeline(drawPipeline);
pass.setBindGroup(0, bindGroup);
pass.draw(4);
pass.end();
device.queue.submit([encoder.finish()]);
}
draw(0.5, 0.5);
function onMove(e) {
const x = e.clientX / canvas.clientWidth;
const y = e.clientY / canvas.clientHeight;
// draw(x, y);
}
function onUp() {
window.removeEventListener('pointerup', onUp);
window.removeEventListener('pointermove', onMove);
}
function onDown() {
window.addEventListener('pointerup', onUp);
window.addEventListener('pointermove', onMove);
}
canvas.addEventListener('pointerdown', onDown);
*/
{"name":"HDR draw (freeze)","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