TL;DR: HTML-in-Canvas is a revolutionary browser proposal that allows native HTML/CSS elements to be rendered directly within a Canvas or WebGL scene. It solves the long-standing “laggy UI” problem for immersive web apps while preserving accessibility and ease of styling.
What is HTML-in-Canvas?
HTML-in-Canvas is a native browser proposal (currently a WICG experiment) that enables the rasterization of DOM elements directly into a <canvas> element’s texture. It allows developers to use standard HTML and CSS to build complex user interfaces that live inside high-performance rendering contexts like WebGL, WebGPU, or 2D Canvas.
Unlike previous attempts to solve this, HTML-in-Canvas doesn’t just “float” the DOM on top; it integrates it into the browser’s native layout and paint pipeline, ensuring frame-perfect synchronization with the rest of your graphics.
The Overlay Dilemma: A 15-Year Head-Ache
If you’ve ever built a 3D scene with Three.js or a data visualization with a 2D Canvas, you’ve faced the “Overlay Dilemma.” You have two bad choices for building UI:
- The DOM Overlay: You position a
<div>over the canvas. It’s easy to style and accessible, but it’s a nightmare to synchronize. As the camera moves, the UI “jitters” or lags because the browser’s DOM layout and the Canvas render loop are running on different schedules. - The Re-implementation Trap: You rebuild buttons, sliders, and text entirely in WebGL. This is fast and synchronized, but it kills accessibility (a11y), breaks SEO, and takes weeks to implement what CSS could do in minutes.
How It Works: layoutsubtree and drawElementImage
The proposal introduces a clean, three-step workflow that feels like the “Holy Grail” of creative coding.
1. Opt-in with the layoutsubtree Attribute
First, you mark your canvas as a layout host. Any children of this canvas are handled by the browser’s layout engine but aren’t rendered to the screen by default.
<canvas id="game-canvas" layoutsubtree>
<div id="player-menu">
<h1>Settings</h1>
<button onclick="save()">Save Game</button>
</div>
</canvas> 2. The paint Event
The browser fires a specialized paint event on the canvas when it’s ready to rasterize content. This ensures you only draw when the browser is ready.
3. Native Drawing
You use the new drawElementImage() method to draw the hidden DOM subtree directly onto the canvas context.
const canvas = document.getElementById('game-canvas');
const ctx = canvas.getContext('2d');
const menu = document.getElementById('player-menu');
canvas.addEventListener('paint', () => {
// Rasterize the HTML div directly into the canvas
ctx.drawElementImage(menu, 10, 10);
}); Comparison: The Three Ways to Render UI
| Feature | DOM Overlays | WebGL UI (Custom) | HTML-in-Canvas |
|---|---|---|---|
| Sync Speed | Laggy / Jittery | Perfect | Perfect |
| Styling | CSS (Easy) | Manual (Painful) | CSS (Easy) |
| Accessibility | Native | Broken | Native |
| Performance | High Overhead | Best | Near-Native |
Why This Matters for the Future Web
Immersive experiences (spatial computing, 3D portfolios, web-based games) have always been second-class citizens when it came to UI. HTML-in-Canvas fixes this by treating the open web’s greatest strength—its document-based accessibility—as a primary input for its highest-performance output.
Frequently Asked Questions
Q: Is HTML-in-Canvas available in all browsers today? A: No. It is currently a WICG proposal and a Chrome experiment. You can test it by enabling specific flags in Chrome Canary, but it is not yet ready for production use across all browsers.
Q: Does it support interactive elements like buttons and text inputs? A: Yes! Because the browser still manages the underlying DOM for layout and hit-testing, native events like click, hover, and text selection work as expected, even though the visual is being rendered inside a canvas.
Q: How does it handle Accessibility (a11y)? A: This is the biggest win. Since the elements are still present in the DOM (hidden under the layoutsubtree attribute), screen readers and other assistive technologies can read and interact with the UI exactly as they would on a normal webpage.
Q: Can I use this with Three.js or other WebGL libraries? A: Absolutely. You can use the drawElementImage rasterization as a dynamic texture source for a Three.js material. This allows you to “wrap” fully interactive HTML forms around 3D objects with zero lag.
Q: What happens to CSS animations inside the canvas? A: CSS animations and transitions work perfectly. Because the drawElementImage call happens during the paint event, the canvas captures the current state of the animated DOM element at the exact moment of the frame render.