SnapDOMGitHub★ 8K
Labs Experiment

The browser paints the capture

SnapDOM's second engine hands the captured clone to Chrome's own renderer through the html-in-canvas API. Compare it with the SVG engine and see which plugins run on it.

TL;DR

snapdom(el, { engine: 'html-in-canvas' }) paints the clone SnapDOM already built, styles resolved and images and fonts inlined, with Chrome's own renderer instead of an SVG image. It runs in Chrome 157 and newer, where the API ships without a flag. Anywhere else, or on a capture this engine cannot paint, SnapDOM uses the SVG engine and you get the same result object. The guide explains when to pick it and what SnapDOM adds on top of the raw API.

Try it

Checking this browser…

Live element

Quarterly report

Revenue grew 12% this quarter.

SVG engine
Not captured yet

html-in-canvas engine
Not captured yet

Type in the field or change the plan, then capture again. Both columns come from the same clone; only the last step differs.

Plugins

A plugin that works on the clone runs as it is: by the time the browser paints, its change is already in the clone. A plugin with a render hook, beforeRender or afterRender, gets the SVG engine on purpose, because it reads the SVG or does its work at that boundary. Those captures still succeed, through SVG.

PluginEngineWhyThis browser
timestamp-overlayhtml-in-canvasAdds its label to the clone
replace-texthtml-in-canvasEdits text in the clone
filterhtml-in-canvasCSS filter on the clone
color-tinthtml-in-canvasOverlay on the clone
redact-inputshtml-in-canvasMasks values in the clone. With blocks or attributes it uses SVG: its final pass runs in beforeRender
agent-maphtml-in-canvasReads the clone, draws on the pixels
context-exporthtml-in-canvasReads the live page
ascii-exporthtml-in-canvasReads the pixels
gif-exporthtml-in-canvas, every frameRecaptures with the same options
video-exporthtml-in-canvas, every frameRecaptures with the same options
streamhtml-in-canvasFull frames come from the engine
html-exportSVGSerializes the clone in afterRender
pdfSVGIts text layer measures the SVG
vectorSVGTakes the clone in beforeRender

How it works

  1. The same clone

    Everything up to the render is the normal pipeline: computed styles become classes, pseudo-elements become real nodes, images and fonts become data URLs, and clone plugins run. The engine choice comes after all of that.

  2. A mount the browser can paint

    The clone goes into a shadow root under <canvas content="drawable">, behind the page. Chrome lays it out like any element, with the page's own form controls, scrollbars and text shaping.

  3. Two frames, one draw

    After one full rendering update the browser holds a snapshot of the mount, and drawElementImage() paints it at your scale and dpr. A blank or unreadable result sends the capture to the SVG engine instead.

  4. The bitmap goes straight to the exports

    toCanvas(), toPng() and the rest draw from the painted canvas. result.url is a PNG data URL, made the first time you read it.

Copy-paste example

npm install @zumer/snapdom
import { snapdom } from '@zumer/snapdom'

const result = await snapdom(document.querySelector('#card'), { engine: 'html-in-canvas' })
const png = await result.toPng()

The engine is part of the default build. In a browser without the API the same option gives you the SVG engine, so you can leave it on without a feature check. Nothing else changes: the options, the result object and the plugins are the ones you already use.

What stays on the SVG engine

This engine paints one plain element box. Captures of body or html, a scrolled root, a root or ancestor with a transform or zoom, a root with a filter, and the outerShadows, clip, width and height options all go through SVG, where that geometry is handled. So do captures in browsers without the API.

html-in-canvas is a WICG specification. Chrome 157 ships it without a flag; before that it was an origin trial and chrome://flags/#canvas-draw-element, and Chromium browsers still on those builds want the older layoutsubtree canvas attribute, which is why SnapDOM sets both names. Firefox and Safari have no implementation, so there every capture uses the SVG engine.