Run the comparison on your own machine
Nothing here is pre-recorded. Pick a scene and the libraries; every number is measured in your browser, now, with the adapters, scenes and pixel oracle the SnapDOM repository uses for its own benchmarks.
How to read it
- One output stage for everyone. Every library is timed through to a PNG data URL, so no side skips rasterization and encoding.
- Cold and repeat are different numbers. First capture builds a new element per run; Repeat captures the same one again. Reuse can change timing and ranking, so read both columns.
- Defaults, scale 1. SnapDOM loads from
@latest; competitors use fixed versions. No library is configured for advantage. SnapDOM's automatic repeat memoization is off in every scene except Polling, so a cached result never races everyone else's full pipeline. - The table does not know who should win. Rows sort by measured time and the fastest is highlighted, whoever it is. On some scenes that is not SnapDOM.
Absolute milliseconds depend on your CPU, browser and how busy the machine is. Quote the ratios.
The page is held still during a run: a library that clones the whole document crops at viewport-relative coordinates, and scrolling mid-capture would move the region.
| Library | First capture | Repeat | vs fastest | Output | Differs from SnapDOM |
|---|
The last column is the share of pixels that differ from SnapDOM's capture beyond a per-channel tolerance. It is not a correctness verdict: SnapDOM is the reference only because this page ships it. Small differences may be antialiasing, font rasterization or a real defect. A large number means the two did not render the same thing; the gallery below shows which. Milliseconds cannot tell a fast capture from a fast capture of the wrong thing.
Every row must also produce the same output size and paint something. Not comparable means a different resolution; blank output means a correctly sized image with nothing in it. Either way the milliseconds measure the wrong thing. Treat those rows as bug reports, not results.
What each library produced. Click one to open it full size. Speed is half the comparison; this is the other half.
Capability matrix, verified by pixels
Feature tables are usually copied from READMEs. This one captures a fixture where each capability paints its own marker colour, then counts those pixels in the output. "Supported" here means the colour survived the round trip.
It starts by proving the oracle can say no: the same fixture is built with every capability removed, and all six must read absent. A checker that cannot fail means nothing.
The oracle self-test runs first.
Cells report behaviour with defaults. Some libraries pass more once configured; html2canvas handles conic gradients with foreignObjectRendering: true. Results vary by engine too: try Safari and Firefox.
What this page cannot tell you
- It is one machine: yours. A busy laptop, a background tab or an extension injecting stylesheets will move every row. Run it twice.
- Competitors load from a CDN. Anything jsDelivr cannot serve shows up as a row that could not load, not as a win.
- Speed is not fidelity. A library that skips shadow DOM has less work to do. Read the output gallery and the capability matrix alongside the milliseconds.
- These are synthetic scenes. Each isolates a cost (per-node work, author-stylesheet scanning, shadow traversal, large-output raster). None of them is your page.
The same scenes run in the repository as __tests__/category.*.benchmark.js. To report a difference, note library versions, browser, scene and capture mode, and open an issue with the copied results and output.
Read the full comparison
Approach trade-offs, the decision guide, and where each library fits.
Back to the comparison View on GitHub