WICG/html-in-canvas: When HTML Stops Living Above the Canvas

A browser proposal that lets real DOM content participate in layout, hit-testing, and accessibility, then get drawn into 2D and 3D rendering pipelines as a native primitive.

8 min read • View on GitHub • More from WICG

A wide editorial illustration of a canvas frame containing a living HTML subtree on one side and its drawn counterpart on the other. A thin alignment line connects the browser-managed DOM to the painted result, showing that semantics stay native while the pixels become drawable.
HTML does not get replaced here. It gets consumed by the canvas while the browser keeps ownership of layout and interaction.
Key Takeaways

The interesting part of WICG/html-in-canvas is not that HTML can appear inside a canvas. It is that HTML can stay HTML while the canvas treats it like a first-class rendering source. That is a different promise from screenshots, overlays, or manual redraws. It is a browser-native bridge between layout and graphics.

Why canvas UIs keep hitting the same wall

Traditional canvas apps have always paid a tax. If you want text, you rebuild layout. If you want inputs, you fake them. If you want selection, focus, or accessibility, you stitch on a parallel system and hope it stays in sync. The result can look good, but it tends to be fragile and expensive.

ApproachFidelityAccessibilityInteractivityMaintenance
Manual canvas UIHigh visual control, low browser helpUsually custom and incompleteEverything must be rebuiltHeavy
html2canvas or screenshot captureGood for snapshots, weak for live UINot nativeMostly staticMedium to heavy
SVG foreignObjectUseful for some layout casesBetter than raster capture, still limitedMixedMedium
DOM over canvas overlayStrong native behaviorStrongGood, but layered and awkwardMedium
html-in-canvasNative HTML behavior with drawable outputNativeNative, with canvas controlLower once supported

That contrast is the whole point. Existing approaches are compromises. This proposal tries to remove the compromise itself.

`layoutsubtree` is the real shift

The foundation is the `layoutsubtree` attribute. It tells the browser to treat the canvas's children as a real layout subtree, which means the engine can do the work it is already good at: sizing, wrapping, hit-testing, focus, and accessibility. The canvas is no longer just a dead rectangle fed pixels. It becomes a container for live DOM that can later be drawn.

The key trick is not just drawing HTML. It is keeping the browser's interactive model aligned with the pixels it draws.

This is where the proposal starts to feel less like a novelty and more like a new rendering model. Layout stays native. The canvas gets a drawable subtree.

`drawElementImage` and `onpaint` close the loop

The paint event is the sync point. In the examples, `onpaint` fires when the subtree changes, the canvas redraws, and `drawElementImage` returns a transform matrix. That matrix is the clever part. Apply it back to the live element, and the invisible DOM lines up with the drawn version. Selection, focus rings, context menus, and hit-testing all stay anchored to the real element.

canvas.onpaint = (event) => {
  ctx.reset();
  const transform = ctx.drawElementImage(drawElement, 0, 0);
  drawElement.style.transform = transform.toString();
};

That return value turns the API from a one-way snapshot into a synchronized system. It is not just rendering. It is keeping browser behavior and drawn output in lockstep.

Nice to see excitement around the proposed html-in-canvas feature, but remember you can already put canvas behind/atop HTML elements. The benefits of this new feature are: ➡️ Readbacks (eg sending to a VideoEncoder) ➡️ Showing HTML on non-planar shapes ➡️ Using shaders on HTML

Jake Archibald, jaffathecake, 106,713 followers · @jaffathecake on X
A close technical cutaway of the html-in-canvas pipeline, with a layout subtree above a paint snapshot and a GPU texture target below. A transform matrix bridges the drawn output back to the live DOM element, showing how interactivity stays synchronized with the canvas image.
The browser does the HTML work first. The rendering pipeline then consumes the result without losing the original DOM relationship.

The same idea scales to WebGL and WebGPU

The proposal gets more interesting when it leaves 2D. In the 3D examples, the HTML subtree can be turned into a texture source. That means a game HUD, a tool palette, a data label, or a text-heavy panel can be authored as real HTML and then fed into a shader pipeline.

This is why the project matters to engine builders and creative app teams. You keep the browser's text layout and accessibility model, but the output can be projected, warped, shaded, or composited like any other graphics asset.

Use caseOld workaroundhtml-in-canvas
Game HUDCustom bitmap text or overlay DOMNative HTML rendered into a texture
Rich chart labelsManual canvas text layoutBrowser layout inside the subtree
Interactive panels in 3DOverlay layers or duplicated UIThe same DOM can participate in the scene
Shader effects on textPre-rasterize, then distortDraw HTML, then feed it forward

That is the practical upside. HTML stops being a layer above graphics and becomes material for graphics.

What it replaces, and what it does not

This proposal is not a generic screenshot API. It is not trying to replace every existing HTML-to-image tool. It is trying to give the platform a native primitive for drawing live, interactive, accessible DOM content into canvas pipelines. That is a narrower goal, but it is the one that removes the most painful hacks.

AlternativeStrengthWeaknessWhy html-in-canvas is different
html2canvasEasy snapshottingRasterizes a page, loses live behaviorThis is a browser primitive, not a capture library
rasterizeHTMLUseful for rendering HTML to canvasStill a workaround with coverage gapsThis keeps the DOM authoritative
SVG foreignObjectCan embed HTML in SVGCompatibility and fidelity limits remainThis is designed around canvas and GPU flows
Overlay DOMStrong native semanticsAwkward for transforms and 3D scenesThis lets canvas consume the content directly

That distinction matters. The best part of the repo is not a clever hack. It is the attempt to make the hack unnecessary.

Why this matters for engine authors and app builders

For browser engineers, html-in-canvas is a bridge between subsystems that usually stay separate. For app builders, it means fewer duplicated code paths and fewer accessibility tradeoffs. The work is still experimental, but the direction is clear: keep HTML native, then let graphics pipelines borrow it.

The first rule of html-in-canvas api, never share anything but a video of your html-in-canvas demo.

Tim Holman, twholman, 25,024 followers · @twholman on X

That joke lands because the demos are compelling. The deeper point is stronger: the web has spent years pretending that rich UI and custom rendering were separate worlds. This proposal argues they do not have to be.