Skip to content

HTML on Canvas — concepts and trade-offs

Diataxis type: explanation. This page builds understanding; for a goal-oriented recipe see the how-to, for lookup facts see the reference.

Why this page exists

"HTML on Canvas" sounds like one technique. It is at least seven, with wildly different maturity and trade-offs. The phrase came up while exploring whether our agent-first web-presence — a chat surface that renders LLM-emitted blocks — could gain from canvas rendering. The short answer reshaped how we think about images on the platform, so this page records the reasoning.

The overloaded phrase

The seven things people mean by "HTML on Canvas" fall into two opposite camps:

  • Putting UI into a <canvas> — either the emerging native browser API that draws live, accessible DOM into a canvas, or canvas-native UI frameworks (Konva, Fabric, PixiJS) that draw widgets with no DOM at all.
  • Turning HTML/data into an image — client-side DOM rasterisers (html2canvas, snapdom), server-side HTML/CSS→SVG→PNG (Satori), the SVG <foreignObject> trick, or a headless browser (Puppeteer/Playwright) producing pixel-perfect PNG/PDF.

The reference catalogues all seven. The interesting conclusion is that the two camps pull in opposite directions for us.

Why we do not put our UI into a canvas

Our web-presence is a public marketing site whose entire thesis is being discovered — by people and by search/LLM crawlers (GEO). Canvas-native UI is the wrong tool for that, because canvas content:

  • is invisible to screen readers (no accessibility tree), and
  • is not in the DOM, so it is not crawlable or indexable.

This is why the generative-UI chat "canvas" we already built is, correctly, a DOM container of components — not an HTML5 <canvas>. Moving it to a real canvas would trade away the two things the site depends on most. The one canvas technique that keeps accessibility — the native "HTML-in-Canvas" API — is a Chromium-only origin trial in 2026 with no second-engine support, so it is a thing to watch, not to ship.

Takeaway: for content surfaces, stay DOM-first. Canvas-native UI is reserved for genuinely graphical, interactive widgets (and even then needs a DOM fallback).

Why the opposite direction is where the value is

The useful idea is the inverse: turn data into an image. Our agent already emits serializable block directives, and Lugar places carry structured fields (photo, place name, caption). That data can become a branded share/social card or an Open Graph image — assets that travel to WhatsApp, social feeds and link previews, which is exactly the distribution a "discovered" site wants.

The decision that matters most for us: render from data, not from the DOM

There are two ways to turn our UI into an image:

  1. Screenshot the live DOM in the browser (html2canvas and friends).
  2. Re-render from the underlying data, server-side (Satori).

For this codebase, (1) is a trap. Our design system leans on glassmorphism (backdrop-filter blur), and DOM rasterisers cannot reproduce backdrop-filter — the glass simply does not appear. A "share this" button built on html2canvas would produce broken-looking cards.

Re-rendering from data with Satori sidesteps the problem entirely: there is no live DOM to misread, only data and a controlled template. This is why the recommended direction is a server-side Satori card-renderer, fed by the same serializable data the UI already has. A spike proved the pattern end-to-end.

Trade-offs worth holding in mind

  • Satori is a CSS subset — flexbox only (no grid, no z-index, no 3D, no RTL), and it needs fonts handed to it explicitly. It is not a general HTML renderer; it is a fast, deterministic card renderer. Design templates to its constraints from the start.
  • Pixel-perfect needs a real browser. For contracts/receipts where fidelity is non-negotiable, a headless browser (Puppeteer) is the honest tool — at the cost of a heavy server dependency. Satori is for templated cards, not arbitrary page capture.
  • Client-side rasterisers still have a place — "export the thing the user is looking at" — but not over our glass UI, and never for anything you want indexed.

Where this lands in the product

The prioritised opportunities (dynamic OG images, Lugar place-cards + photo watermarking, chat/itinerary share cards) are tracked as Riffs #218–#220 and timed with the apex go-public (Plan #028 Slice I). The full landscape, surface-by-surface map and sources live in the research note.