The Problem It Solves

Imagine you are building a browser-based 3D game using WebGL or a modern interactive canvas app. You want to display an in-game computer screen, or maybe just a settings menu. You quickly hit a wall:

HTML handles text, forms, scrolling, and accessibility perfectly. But you cannot directly draw an HTML <div> onto a <canvas>.

Instead, developers historically had to do one of two things:

  • The CSS Overlay Hack: Floating HTML elements on top of the canvas using CSS position: absolute;. (This doesn't work if the element needs to be inside a 3D world).
  • Reinventing the Wheel: Writing thousands of lines of complex JavaScript graphics code to manually draw text, fake input cursors, and mimic scrollbars entirely inside the canvas.

The Solution: HTML-in-Canvas

The HTML in Canvas API is an experimental proposal (incubated by the WICG) created exactly for this. It natively acts as a bridge between the DOM and your Graphics.

It allows the browser to take a live, high-fidelity snapshot of HTML elements that are descendants of the canvas and hand them directly to the graphics context as a texture.

Because the HTML elements remain in the DOM tree as actual children of the <canvas>, they remain fully accessible to screen readers, focusable via keyboard, and interactive, even though they are visually rendered inside the canvas buffer.

Note: The HTML in Canvas API is a living proposal (WICG). The attributes and methods described below are experimental, subject to change, and currently implemented behind a flag in Chromium browsers.

The Proposed Primitives

The emerging proposal introduces several key primitives to function securely and efficiently:

1. The layoutsubtree Attribute

To opt-in, you add the layoutsubtree attribute to the <canvas> element. This tells the browser that the canvas children should participate in standard layout and hit testing. It creates a stacking context for the children, ensuring they are prepared for capture without being rendered in the normal page flow.

2. The Paint Event & requestPaint()

Instead of manual loops, the API uses a paint event that fires only when the target HTML visually changes. You can also manually trigger a re-render using canvas.requestPaint(), which functions similarly to requestAnimationFrame().

3. Rasterization Methods

The proposed methods allow you to draw the HTML content into different graphics contexts:

  • 2D Canvas: Uses ctx.drawElementImage(element, x, y).
  • WebGL: Uses gl.texElementImage2D(...) to turn the element into a 3D texture.
  • WebGPU: Uses copyElementImageToTexture().

4. captureElementImage()

For advanced use cases like OffscreenCanvas in web workers, you can capture an ElementImage snapshot. This object can be transferred to a worker thread and drawn independently of the main thread.

Code Example (Proposed Syntax)

This snippet demonstrates the latest proposed syntax. Note how drawElementImage returns a DOMMatrix, which is used to synchronize the element's actual position in the DOM with its visual position in the canvas.

Rendering DOM to Canvas
<!-- Note: This syntax is speculative and requires Chrome Canary with #canvas-draw-element enabled --> <!-- Step 1: The Canvas with 'layoutsubtree' and HTML children --> <canvas id="output-canvas" width="400" height="200" layoutsubtree> <div id="ui-card" style="padding: 20px; background: cyan; width: 200px;"> <h2>Interactive HTML</h2> <input type="text" placeholder="Type here..." /> </div> </canvas> <script> const canvas = document.getElementById('output-canvas'); const uiElement = document.getElementById('ui-card'); const ctx = canvas.getContext('2d'); // Step 2: Listen for the 'paint' event canvas.onpaint = (event) => { ctx.clearRect(0, 0, canvas.width, canvas.height); // Step 3: Draw the element. It returns a transform matrix. const transform = ctx.drawElementImage(uiElement, 50, 50); // Step 4: Synchronize DOM location with drawn location // This ensures clicks and accessibility focus land in the right spot! uiElement.style.transform = transform.toString(); }; </script>

Privacy-Preserving Painting

To prevent security leaks (like cross-origin data theft), the API implements "privacy-preserving painting." When an element is drawn to a canvas, certain sensitive information is automatically excluded:

  • Cross-Origin Data: Iframes, images, or CSS url() references from different origins will not be painted.
  • User Secrets: Pending form autofill information and spelling/grammar markers are hidden.
  • History: Visited link information (which could reveal a user's browsing history) is treated as unvisited.
  • Anti-Aliasing: Subpixel text anti-aliasing is disabled to prevent timing attacks that could reveal sensitive pixels.

If this API makes its way out of incubation and into modern browsers, rendering complex components into games, VR headsets, 3D scenes, or static image exports will become a native and blazing-fast part of the web. For reliable updates, follow the official WICG html-in-canvas living explainer.

To test this today: The API is implemented behind a flag in Chromium browsers (like Chrome Canary). You can enable it by navigating to chrome://flags/#canvas-draw-element.