33 comments

[ 5.3 ms ] story [ 57.8 ms ] thread
Regardless of the subject matter, the tweets announcing this are a masterclass in demoing why an architectural/platform improvement can be impactful.
Some details on how it works from a code comment:

Problem: DOM-based text measurement (getBoundingClientRect, offsetHeight) forces synchronous layout reflow. When components independently measure text, each measurement triggers a reflow of the entire document. This creates read/write interleaving that can cost 30ms+ per frame for 500 text blocks.

Solution: two-phase measurement centered around canvas measureText.

prepare(text, font) — segments text via Intl.Segmenter, measures each word via canvas, caches widths, and does one cached DOM calibration read per font when emoji correction is needed. Call once when text first appears.

layout(prepared, maxWidth, lineHeight) — walks cached word widths with pure arithmetic to count lines and compute height. Call on every resize. ~0.0002ms per text.

https://github.com/chenglou/pretext/blob/main/src/layout.ts

Has someone ever found a good solution for long / infinite lists / grids virtualization not breaking browsers native text search?

Maybe for this we need a new web "Search" API instead of JS. Not sure it can be done otherwise without browser's help.

Lol improving the DOM and HTML instead of just tossing it into JS? WHATWG will be rolling on the floor for days.
This is awesome! I had this problem when building a datagrid where cells would dynamically render textarea. IIRC I ended up doing a simple canvas measurement, but I had all the text and font properties static, and even then it was hellish to get it right.
Love this. I especially liked shape based reflow example.

This is something I've been thinking for ages and would love to add to Ensō (enso.sonnet.io), purely because it would allow me to apply better caret transitions between the lines of text.

(I'm not gonna do that because I'm trying to keep it simple, but it's a strong temptation)

Now a CSS tangent: regarding the accordion example from the site (https://chenglou.me/pretext/accordion), this can be solved with pure CSS (and then perhaps a JS fallback) using the `interpolate-size` property.

https://www.joshwcomeau.com/snippets/html/interpolate-size/

Regarding the text bubbles problem (https://chenglou.me/pretext/bubbles), you can use `text-wrap: balance | pretty` to achieve the same result.

(`balance` IIRC evens out the # of lines)

Quick overview of pretext: if you want to layout text on the web, you have to use canvas.measureText API and implement line-breaking / segmentation / RTL yourself.

Pretext makes this easier. Just pass the text and text properties (font, color, size, etc) into a pure JS API and it layouts the content into given viewport dimension.

Earlier you'll have to either use measureText or ship harbuzz to browser somehow. I guess pretext is not a technical breakthrough, just the right things assembled to make layouting as a pure JS API.

I have one question though: how is this different from Skia-wasm / Canvaskit? Skia already has sophisticated API to layout multiline text and it also is a pure algorithmic API.

This thing is very impressive.

The problem it solves is efficiently calculating the height of some wrapped text on a web page, without actually rendering that text to the page first (very expensive).

It does that by pre-calculating the width/height of individual segments - think words - and caching those. Then it implements the full algorithm for how browsers construct text strings by line-wrapping those segments using custom code.

This is absurdly hard because of the many different types of wrapping and characters (hyphenation, emoji, Chinese, etc) that need to be taken into account - plus the fact that different browsers (in particular Safari) have slight differences in their rendering algorithms.

It tests the resulting library against real browsers using a wide variety of long text documents, see https://github.com/chenglou/pretext/tree/main/corpora and https://github.com/chenglou/pretext/blob/main/pages/accuracy...

> It does that by pre-calculating the width/height of individual segments - think words - and caching those.

From the description, it doesn’t calculate it, but instead renders the segments in canvas and measures them. That’s still relatively slow compared to what native rendered-text-width APIs will do, and you have to hope that the browser’s rendering will use the identical logic in non-canvas contexts.

I mentioned this elsewhere, but it's not actually doing any of what you describe. It's rendering the text to a canvas and then measuring that. I don't see any benchmarks that indicate it's faster than just sticking it in a <p> tag, and not any clear indication that it would be. It's certainly not implementing the full algorithm for text rendering in a browser.

It certainly seems to provide an API for analysing text layouts, but all of the computation still goes through the browser's native layout system.

Even when it is rendered... I have my text/zoom level pretty close to maxed out on Android as my issues are mostly retinal, not correctable with glasses... and so many apps will completely scroll my long inputs off screen while I'm trying to input... or otherwise make it impossible to see/edit after I've entered a handful of lines. It's infuriating to say the least.
I said it elsewhere but will repeat it here:

This is incredibly impressive, many of this things have been missing for forever! I remember the first time I couldn't figure out how do a proper responsive accordion, it was with bootstrap 1, released in 2011 !! Today it's still not properly solved (until now?).

Many of thing things belong in css no in js, but this has been the pattern with so many things in the web

1) web needs evolve into more complex needs 2) hacky js/css implementation and workarounds 3) gets implemented as css standard

This is a not so hacky step 2. Really impressive,

I would have thunk that if this was actually possible someone would have done it already, apparently not, at some point I really want to understand what's the real insight in the library, their https://github.com/chenglou/pretext/blob/main/RESEARCH.md is interesting, they seem to have just done the hard work, of browser discrepancies to the last detail of what does an emoji measure in each browser, hope this is not a maintenance nightmare.

All in all this will push the web forward no doubt.

Yeah, that's definitely needed.

That's why I've added Graphics.Text (https://docs.sciter.com/docs/Graphics/Text) in Sciter.

Graphics.Text is basically a detached <p> element that can be rendered on canvas with all CSS bells and whistles.

This should be standard functionality offered by browsers. How do you make feature requests to W3C, and do they allow the community to vote on feature ideas?
I've had to approximate text size without rendering it a few times and it's always been awkward, I'm glad there's something to reach for now (just hoping I remember that this exists next time I need it)
only took 30+ years to get (back) to this point.

wasted generation.

(comment deleted)
By the author of the library

> This was achieved through showing Claude Code and Codex the browsers ground truth, and have them measure & iterate against those at every significant container width, running over weeks

https://x.com/_chenglou/status/2037715226838343871?s=20

There was another comment about using Autoresearch probably for this but I might be misremembering

This feels like a great example of a project that wouldn't exist if not for AI coding.
Gosh, I wish this had existed a year ago; I spent an absurd amount of time creating a system for print brochure typesetting in HTML, that would iteratively try to find viable break points (keeping in mind that bullets etc. could exist at any time) that would ensure non-orphaned new lines, etc., all by using the Selection API and repeatedly finding bounding boxes of prospective renders.

It works, and still runs quite successfully in production, but there are still off-by-one hacks where I have no idea why they work. The iterative line generation feature here is huge.

The most practical use case is the text bubble wrapping one. That’s always frustrating when you want to wrap text inside any box with a border or background color (like a button or a “badge” component).
With all the multi$ efforts invested in the browsers, what explains that such basics as text layout are neglected, requiring libraries such as this one?
I did ~this in the 200X’s for a C# todo list, not nearly as polished but it was still So Much Work. Filed under things that should just work already.
I get the feeling this is an AI hallucination. It uses the canvas to render the text to be measured, which doesn't bypass the browser layouting. The only potential performance to be gained here is rendering straight to a canvas instead of building a dom node, but it's not clear that it's actually faster. I can't imagine the cost of a single <p> is that large, and I'm not certain that it's slower than whatever steps the canvas API uses to turn text into pixels.
I was just writing a long comments about how the "const prepared = prepare('AGI 春天到了. بدأت الرحلة ', '16px Inter')" example makes no sense, as the font name is weirdly split across both arguments, but theres some right-to-left stuff going on. The font is "16px Inter", and the arabic and emoji are part of the first argument
I saw this amazing project in the news yesterday, and I've already implemented it on my personal blog today.