demo · v138
paintsubtree event loop
The third new primitive of HTML-in-canvas is the paintsubtree event. It fires when the engine has re-laid-out the captured subtree — the signal you need to schedule a re-upload to the canvas / texture without polling. This is what makes the pipeline live.
Behind a flag: ships in Chrome 138 with
chrome://flags/#enable-experimental-web-platform-features. Fallback: a polling loop at 30Hz mirrors the event so the demo is meaningful without the flag.
probing paintsubtree event…
Source DOM — a live dashboard
Production status
CPU 0%
Memory 0%
Network 0 req/s
last tick: never
2D canvas — re-uploaded on paintsubtree
paintsubtree fires 0×/s
Event log
The shape of the loop
const canvas = document.getElementById('canvas');
canvas.layoutSubtree = true;
const ctx = canvas.getContext('2d');
const src = document.getElementById('src');
canvas.addEventListener('paintsubtree', () => {
// engine just re-laid-out the subtree
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawElement(src, 0, 0); // re-rasterise into the canvas
});
// Updates to src — text content, css, style — implicitly schedule
// a paintsubtree event on the next frame. No polling needed.
setInterval(() => {
document.getElementById('cpu').textContent = Math.random() * 100 | 0;
}, 500);
What's happening
- The dashboard's numbers, bars, and timestamp update every tick rate ms via setInterval.
- Each mutation invalidates the captured subtree. The engine waits until the next frame, lays it out, then fires
paintsubtreeon the canvas. - The event handler re-rasterises (via
ctx.drawElement) — exactly once per frame even if multiple mutations happened. No double-rendering, no missed updates. - The event-rate counter shows the actual fire rate matches the mutation rate up to vsync (~60Hz).
- This is the "loop" that lets canvas-based apps composite live data — game HUDs, chart annotations, AR overlays — without the choice between "JS polyfill" and "static screenshot".