demo · v138

WebGL texture feed

The 2D canvas use case is the obvious one. The interesting one is using a live DOM subtree as a WebGL texture — texElementImage2D() uploads the rasterised element straight into a GPU texture, so you can wrap a real form, a live <input>, or a styled article onto a 3D mesh.

Behind a flag: ships in Chrome 138 with chrome://flags/#enable-experimental-web-platform-features. Fallback uses a striped procedural texture so the geometry still spins.
probing texElementImage2D…

DOM subtree (real HTML — type into it)

Live HTML subtree

This is an actual div. Try focusing the input — text caret, selection, focus ring all behave the way they should.

accessible selectable type-able

WebGL canvas — texture sampled from above

The shape of the API

const canvas = document.getElementById('gl');
canvas.layoutSubtree = true;  // opt the canvas in
const gl = canvas.getContext('webgl2');

const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);

// upload the live DOM element into the GPU texture
gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA,
                     gl.RGBA, gl.UNSIGNED_BYTE,
                     document.getElementById('ui'));

canvas.addEventListener('paintsubtree', () => {
  gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA,
                       gl.RGBA, gl.UNSIGNED_BYTE,
                       document.getElementById('ui'));
});

What's happening

  1. Three new primitives ship: canvas.layoutSubtree = true (opt-in), drawElement / texElementImage2D / copyElementImageToTexture (upload), and a paintsubtree event (re-upload signal).
  2. The DOM subtree is laid out and rasterised by the engine. Then it lands in a 2D context, a WebGL texture, or a WebGPU texture.
  3. Crucially, the original subtree is still in the page. It accepts focus, keyboard, AT — everything you lose with html2canvas.
  4. For this demo: the WebGL canvas mounts a textured quad that spins. The texture comes from the live left pane — type into the input and the texture updates next frame.

see also