HTML in Canvas Experiments
What ctx.drawElementImage() could do for visual regression
testing. Each page runs live in this tab, no Playwright. Needs Chrome
157+, where HTML in Canvas shipped. Numbers below are from the
Playwright runs in experiments/ on 2026-11-06.
-
PoC
Diff two HTML snippets
Draw two snippets onto canvases and compare them with pixelmatch, without a screenshot or a PNG round trip.
On 13 rendering cases (text, shadow, blur, emoji, form controls, SVG…) the drawing matched
Open →page.screenshotbyte for byte. -
Experiment 02
Which component caused the diff?
Draw every component on its own. A drawable descendant is left out of its ancestor's layer, so a component is judged on its own pixels, and its position separately.
A whole-image diff painted 8,162 px red below a grown notice. By component: 3 changed (the causes), 3 moved 0, 21 (pushed, pixels unchanged).
Open → -
Experiment 04
Stop-motion capture of an animation
Pause, seek, wait one rendering frame, draw: 61 frames of four CSS animations, each measured from its pixels against its timing function.
Seek + 1 frame: 0.4 px worst error. Drawing straight after a seek gives the same frame every time; drawing inside
Open →requestAnimationFrameis exactly one frame behind. -
Experiment 05
Diff through a colour-vision lens, on the GPU
A WebGL2 shader applies a colour-vision-deficiency matrix and pixelmatch's colour delta per pixel, checked against pixelmatch on the CPU.
A red→green status badge differs by 0 px through deuteranopia: the change is carried by colour alone. With a ✕/✓ label, 23% survives.
Open →
Learned building these
-
The shipped API needs
content="drawable"on the canvas anddrawableon every element passed todrawElementImage. -
A drawable element's
getBoundingClientRect()is where it was last drawn; read its layout position fromoffsetLeft/offsetTop. - The page's CSS applies to the canvas subtree. A shadow root isolates it.
-
The canvas must be painted: off-screen to the left it draws transparent
with no error, and
visibility: hiddenthrows. - Media queries follow the page viewport, not the canvas size.
- The drawing is scaled by bitmap size / CSS size: keep a drawable canvas at its natural CSS size.
Details and full tables: experiments/README.md.