Engineering notes

One widget, three renderers: what a fair comparison needs

Leo Marin
10 October 2026 · 6 min read

A system web view, a Rust layout engine and a C++ one all draw the same card. The numbers they produce are only comparable when the card is one they all draw the same way.

The problem with the old corpora

Our generated corpus was written for a browser. Seventeen of its forty widgets use display: grid, several draw their charts with inline SVG, one flips a flashcard with a 3D transform, and almost all of them lean on box-shadow for depth. Loading those files on litehtml or Blitz gives a time, but not a comparable one: an engine that skips the SVG does less work, an engine that collapses the grid into a column does more, and neither picture is what the user would see.

The Telegram demos are closer, since they were written against litehtml in the first place, but they also depend on a host stylesheet, an action prelude and a handful of image schemes that mean nothing outside that app.

A benchmark where one contestant quietly skips half the course is not a benchmark.From the review of the first Blitz numbers

What the common profile keeps

The intersection is larger than it sounds. Every engine in the comparison lays out block and inline content, flexbox with wrapping and growth, tables with collapsed borders, lists, borders with radii, solid and gradient backgrounds, var() custom properties, overflow: hidden, and relative or absolute positioning. The theme switches through a media query, which the validator rewrites for every renderer.

Kept out on purpose

What the files exercise

Four groups, each aimed at a different cost. The cards are static layouts of realistic density: a profile, a receipt, a timetable, a dashboard. The interactive files change state on a tap: a tab swap, a poll bar growing, a calendar rebuilt from a string. The live files keep going without input: a clock at 1 Hz, bars filling with requestAnimationFrame, a ticker at 2 Hz, a log appending lines. The heavy files are the ones that make a layout engine sweat: a 60-row table, 300 chips, a deep tree, this article.

  1. Start-up: from host creation to the first frame with content.
  2. A text-only update: the clock, the counters, the ticker.
  3. A structural update: a rebuilt list, a swapped panel, an appended log line.
  4. A full relayout under load: sorting the long table, expanding the whole tree.

For litehtml every update is the fourth kind, because the engine rebuilds the document from a string; that is the real cost of its model and the corpus should make it visible rather than hide it.

Reading the results

renderer   first paint   text update   rebuild
webview        121 ms          n/a         n/a
blitz           82 ms          ?            ?
litehtml       107 ms          ?            ?

The first column is from the Android bench on the old corpus; the other two are what this corpus is for. Judge the pictures by eye, then trust the timings.


Numbers above are illustrative placeholders. The measured tables live next to the bench scripts.

renderingbenchmarkslitehtmlblitzwebview