Tearline

Try it

Tearline

Playground

Docs

API

FAQ

Try it

Tearline

Playground

Docs

API

FAQ

Try it

Method

Serialise the node.
Paint the SVG.

the whole technique

Turning part of a page into a PNG without a server is four browser APIs and no library. You serialise the element to XHTML, wrap that string in an SVG <foreignObject>, hand the SVG to an Image as a data URI, and draw the decoded image onto a canvas. The canvas gives you a Blob or a data URL.

The reason this works at all is that a browser will happily rasterise arbitrary HTML if you ask it to as part of rendering an SVG. You are not reimplementing a layout engine — you are borrowing the one already in the room. That is also why it is fast, and why the output matches what the user saw rather than approximating it.

the trade

Because the SVG is loaded as a data URI, it is a sandbox with no origin. It cannot fetch an image, a font or a stylesheet. Everything the picture needs has to be inside the string before you serialise it. That single constraint is behind almost every why is my export blank question, and it has its own section below.

rasterise.js

rasterise.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 1. serialise — XMLSerializer, never innerHTML
const xhtml = new XMLSerializer().serializeToString(node);
// 2. wrap it in an SVG foreignObject
const NS = 'http://www.w3.org/2000/svg';
const svg =
`<svg xmlns="${NS}" width="${w}" height="${h}">` +
`<foreignObject width="100%" height="100%">` +
`${xhtml}</foreignObject></svg>`;
// 3. decode it as an image
const img = new Image();
const uri = 'data:image/svg+xml;charset=utf-8,';
img.src = uri + encodeURIComponent(svg);
await img.decode();
// 4. paint to a canvas and read the PNG back
const canvas = document.createElement('canvas');
canvas.width = w * scale; canvas.height = h * scale;
const ctx = canvas.getContext('2d');
ctx.scale(scale, scale); ctx.drawImage(img, 0, 0);
canvas.toBlob((blob) => /* your PNG */, 'image/png');

Limits

Why your export
comes out blank.

what silently does not survive

a remote <img>The SVG is loaded as a data: URI, so it has no origin that can fetch anything. Inline the image as a data: URI before you serialise.
a webfontSame sandbox: an @font-face pointing at a URL will not be fetched. The text renders in a fallback face, or in nothing.
a stylesheetStyles are not inherited into the foreignObject from the parent document. Whatever CSS you need has to be inlined into the serialised markup.
innerHTMLA foreignObject is parsed as strict XML. Void elements written HTML-style — <hr>, <br>, an unquoted attribute — are fatal parse errors, and the image simply fails to decode. XMLSerializer is the only serialiser that self-closes and namespaces correctly.
a live animationWhatever frame the element is on when you serialise is the frame you get. Freeze or disable animations on the clone first.

the other failure

There is a second, louder way this breaks, and it is a security rule rather than a sandbox one. Per MDN, as soon as you draw data loaded from another origin without CORS approval, the canvas becomes tainted, and calling toBlob(), toDataURL() or captureStream() on it throws a SecurityError. The pixels are on screen; you are just not allowed to read them back. This protects people from having a page use images to pull data out of remote sites without permission.

So the two failure modes look different and want different fixes. A blank or half-drawn PNG means something did not load inside the sandbox — inline it. A thrown SecurityError means something cross-origin did load and poisoned the canvas — serve it same-origin, or as a data URI, or with CORS headers and crossOrigin set.

a note on scale

Set the canvas to a multiple of the measured size and scale the context by the same factor before drawing. Exporting at 1× on a retina display is the most common reason a share image looks soft next to the page it came from.

Alternatives

Four packages.
Today's numbers.

general-purpose · version, deps, licence · published, size

modern-screenshot4.7.0 · 0 deps · MIT16 Apr 2026 · 186 KB
html-to-image1.11.13 · 0 deps · MIT14 Feb 2025 · 315 KB
html2canvas1.4.1 · 2 deps · MIT22 Jan 2022 · 3.38 MB
dom-to-image2.6.0 · 0 deps · MIT4 Oct 2017 · size not reported

Registry facts, not a review: latest version, declared runtime dependencies, licence, publish date and unpacked size, read from registry.npmjs.org on 31 July 2026. All four are MIT, and three of them declare no runtime dependencies at all.

html2canvas is the exception on both counts — it pulls in css-line-break and text-segmentation, and it is roughly ten times the size of the others on disk. It is also the one that has gone longest without a release apart from dom-to-image, whose latest version is more than eight years old. None of that makes either one broken; plenty of people ship both. It is just what the registry says today.

If you want a general-purpose rasteriser you can point at any node, one of these four is the right answer and Tearline is not.

where Tearline fits

Tearline is not a general rasteriser. It is a custom element that renders whatever you wrap in it as a thermal receipt — paper texture, torn edge, barcode — and then exports that using the technique above. The export is a feature of the component, not a library you can point at an arbitrary div.

Which makes the choice easy. Need a PNG of some existing part of your page? Use one of the four. Need the receipt look — a wrapped-up listening history, an order summary, a share card built out of rules and monospace — and want the export to come with it? That is the whole of what this is for.

the export API

export.js
1
2
3
4
5
const el = document.querySelector('tear-line');
await el.toBlob({ scale: 2 }); // Blob
await el.toDataURL(); // string
await el.download('receipt.png'); // saves it

The full attribute and method reference is in the documentation, and the component itself is served unminified at /tearline.js. How the receipt look itself is built — the paper, the type and the torn edge, in copyable CSS — is a separate write-up.

Sources

Every number here.
Fetched, not remembered.

checked 31 July 2026

registry.npmjs.orgVersion, licence, runtime dependency count, publish date and unpacked size for html-to-image, modern-screenshot, html2canvas and dom-to-image. Fetched 31 July 2026.
developer.mozilla.orgThe tainted-canvas rule: drawing cross-origin data without CORS approval makes toBlob(), toDataURL() and captureStream() throw a SecurityError. Fetched 31 July 2026.
tearline.kynth.studio/tearline.jsThe Tearline implementation described above — 12.7 KB, served unminified, HTTP 200 on 31 July 2026. It is the whole component; there is nothing else to read.

why this is here

Package versions and publish dates go stale, and a page that quotes them from memory is wrong within weeks without ever looking wrong. Every figure above carries the date it was read, so you can tell at a glance how much to trust it — and so can we, because this page is on a register that gets re-checked against these same sources.

If a number here has drifted, the source link is the authority, not this page.

Tearline

Wrap any HTML in one tag and it prints as a receipt.

Product

Overview

Features

Playground

FAQ

Docs

Install

API reference

Exporting a PNG

Accessibility

DOM to PNG, explained

Receipt-style UI

Spotify receipt generators

Code

Read the source

llms.txt

MIT licence

Studio

Kynth Studios

© 2026 Tearline. MIT licensed — free forever.

A Kynth Studios project

Tearline

Wrap any HTML in one tag and it prints as a receipt.

Product

Overview

Features

Playground

FAQ

Docs

Install

API reference

Exporting a PNG

Accessibility

DOM to PNG, explained

Receipt-style UI

Spotify receipt generators

Code

Read the source

llms.txt

MIT licence

Studio

Kynth Studios

© 2026 Tearline. MIT licensed — free forever.

A Kynth Studios project

Tearline

Wrap any HTML in one tag and it prints as a receipt.

Product

Overview

Features

Playground

FAQ

Docs

Install

API reference

Exporting a PNG

Accessibility

DOM to PNG, explained

Receipt-style UI

Spotify receipt generators

Code

Read the source

llms.txt

MIT licence

Studio

Kynth Studios

© 2026 Tearline. MIT licensed — free forever.

A Kynth Studios project