typeset: a Knuth and Plass line breaker running in the browser
TeX line breaking algorithm in JavaScript
At a glance
- What is it?
- typeset is a JavaScript implementation of the Knuth and Plass line breaking algorithm that sets justified text in a browser with less inter-word spacing than native text-align justify. It also does ragged right, centred setting, variable line widths and text inside shapes, but it ships as source with no package releases.
- Who is it for?
- typeset suits a developer building a long-form reading surface where the native justified spacing is visibly uneven, and who is prepared to integrate a source tree rather than install a package. It does not suit a team that needs a maintained npm dependency, automatic hyphenation dictionaries, or handling for paragraphs that change while the reader is typing, since the dynamic paragraph problem is still on the to-do list.
- Can I use it commercially?
- Yes. BSD-2-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 126 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Why the browser's justify is not good enough
A browser can justify text with a single CSS declaration, so the question is why anyone would implement a line breaking algorithm in JavaScript. The answer the project gives is measurement. The README compares a paragraph set by the Knuth and Plass implementation with the same paragraph set by a browser using `text-align: justify`, and reports that the browser lines have, on average, greater inter-word spacing, and that the browser ends up with ten lines instead of the nine found by the algorithm.
The project is careful about the fairness of that comparison. The browser's default inter-word space is not public, nor are its stretching and shrinking parameters, so the author notes the comparison might not be completely fair, while adding that experimental results indicate the values used in most browsers are either identical or very similar to the ones assumed.
So the goal is stated as optimally setting justified text in the browser, with the wider ambition of providing a library for various line breaking algorithms in JavaScript. The audience is developers who care about the visual result of long-form text, mostly in the browser.
The comparison is illustrated with an image that annotates each line with its stretching or shrinking ratio against the optimal line width, using a default space of 1/3 em, a stretchability of 1/6 em, and a shrinkability of 1/9 em.
Boxes, glue and penalties: how the algorithm decides
The implementation follows the classic TeX model, where a paragraph is a sequence of items rather than a string. Boxes are characters with a fixed width, glue is the space between words and carries both a natural width and a stretchability and shrinkability, and penalties describe how bad it is to break at a point, such as a hyphen.
The algorithm then optimises the whole paragraph at once rather than each line in isolation, which is the source of the difference against a browser. A browser justifies line by line, so it can produce a locally acceptable line that forces an ugly line later. The Knuth and Plass approach minimises the total badness across the paragraph, which is why the inter-word spacing comes out more even and why a line can occasionally be slightly loose to avoid a very tight one further down.
The box, glue and penalty model is also what makes the rest of the project possible. The README states the algorithm is not only capable of justifying text, it can perform all sorts of alignment with an appropriate selection of boxes, glue and penalties, and that varying line widths can be used to flow text around illustrations, asides or quotes. Changing the line width at a point in the paragraph and reverting it later leaves a gap for an illustration, and in that example the algorithm also chose to hyphenate certain words to reach acceptable line breaking.
Measuring the browser's own line breaks
To compare against the browser at all, the project has to find out where the browser broke its lines, and the README is upfront about the difficulty: there is no API to retrieve the positions of the line breaks the browser inserted, so the approach resorts to trickery.
The trick is to wrap every word in an invisible `<span>` element and read back its `y` position. If the `y` position of the current word differs from that of the previous word, a new line has started, which splits the paragraph into individual lines.
The ratios then come from a second measurement. The width of each line is measured twice, once with `text-align` set to `justify` and once with it set to `left`, and the difference is divided by the amount of stretchability in the line, which is the number of spaces multiplied by the stretch width for spaces. Since the real stretchability is unknown, the comparison uses 1/6 em, the same value the Knuth and Plass implementation assumes.
This measurement code is a second use case in its own right. It tells you exactly how far each browser line is from the optimum, which is what makes the visual comparison in the README reproducible rather than a matter of opinion.
Correcting browser line breaks with word-spacing
The second use of the algorithm is corrective rather than generative. Instead of replacing the browser's layout, the algorithm can compute the ideal breaks and then adjust the browser's own rendering to match. The easiest route is to split the text into lines and set the CSS `word-spacing` property per line.
There is a catch, and it is a browser limitation rather than an algorithmic one. Webkit based browsers do not support sub-pixel word-spacing, so the per-line adjustment cannot express the fine-grained differences the algorithm computes. The workarounds are to absolute position each word, or to split the line into segments with integer word spacing. The project points at its own Flatland example for the segmented approach, and the examples directory holds an `article` example and a `flatland` example.
That limitation shapes the practical advice. On a Webkit engine, you are pushed toward absolute positioning, which is more code and more fragile with respect to text reflow, so the benefit of the algorithm shrinks. Any evaluation of this project should test on the actual browser you target rather than assume the CSS route works everywhere.
Ragged right, centred and text shaped into figures
The most striking part of the project is that it is not limited to justified text. Ragged right is not simply justified text with fixed inter-word spacing; the algorithm instead tries to minimise the amount of white space at the end of each sentence across the whole paragraph, and it attempts to reduce the number of words sticking out into the margin. Ragged left is obtained by taking a ragged right setting and aligning the line endings with the left border.
Centred text follows the same logic, being an attempt at minimising line lengths over the whole paragraph rather than a mechanical centring of justified lines.
Then there is the variable line width mode, where the line width changes along the paragraph. The README shows text flowing around an illustration by temporarily shortening the line widths and then reverting them, and shows non-rectangular shapes built the same way: an increasing line width with each line centre aligned produces a triangular shape, and basic math extends the trick to circles and arbitrary polygons, with an example of text set inside a circle.
This is what separates typeset from a justification utility. The same optimiser, with different line widths and different break costs, produces a page layout rather than a single text column.
No release, no package, and an unfinished to-do list
Two facts shape any adoption decision. First, the repository has no GitHub releases, and there is no package manifest, no build configuration and no install instructions anywhere in the README or the top level file listing. The tree contains `src/`, `lib/`, `examples/`, the README, the licence and a set of PNG screenshots, so integration means vendoring the JavaScript into your own project and wiring it up yourself. The last push was on 2026-06-20, so the code is not frozen, but there is no versioned artefact to point a dependency at.
Second, the author lists what is not implemented yet. Hanging punctuation, which the original paper explains can be done in the box, glue and penalty model by giving the punctuation a zero-width box and moving the symbol width into the following glue, is on the to-do list with the paper's explanation attached. So is a quality comparison against the `text-justify` CSS property, and the hardest of the three: how to deal with dynamic paragraphs, since the ratios change during editing and the text visibly moves around.
That last item is the one to weigh most heavily. A static article justifies well with this algorithm. A text editor that reflows as the user types is exactly the case the author has not solved.
The licence is BSD-2-Clause, which is permissive and lets you vendor the source into a larger project without copyleft obligations, provided you keep the licence and copyright notice.
Getting the code into your page
There is no install step to describe, so the honest route is to take the source. Clone the repository, and work from the `src/` and `lib/` directories, which hold the implementation, with the two example pages under `examples/` as your reference integration.
The `examples/flatland/` directory is the one to study first if you are on a Webkit browser, because it demonstrates the segmented word-spacing approach that works around the missing sub-pixel support. The `examples/article/` directory shows the ordinary case of a long-form document.
There is no build step to run, no compiler flag to set and no bundler configuration to copy, because the project is plain JavaScript with no declared dependencies. The only tooling requirement in the repository is a browser capable of rendering the examples.
One thing to check before you rely on it: the README notes the browser's default inter-word spacing and stretchability are not exposed, so any measurement you make against a real browser is an estimate. The examples let you see the difference directly, which is the only reliable way to judge whether the improvement matters for your own font and measure.
Editorial conclusion
typeset suits a developer building a long-form reading surface where the native justified spacing is visibly uneven, and who is prepared to integrate a source tree rather than install a package. It does not suit a team that needs a maintained npm dependency, automatic hyphenation dictionaries, or handling for paragraphs that change while the reader is typing, since the dynamic paragraph problem is still on the to-do list. Before using it, read the algorithm comments in `src/` and `lib/`, run the `examples/article` and `examples/flatland` pages in your target browser, and confirm that your Webkit-based browsers still lack sub-pixel word-spacing, which is what forces the absolute positioning fallback.
Frequently asked questions
What does the typeset project implement?
It is a JavaScript implementation of the Knuth and Plass line breaking algorithm, the classic algorithm used in TeX, with the goal of optimally setting justified text in the browser and providing a library for various line breaking algorithms in JavaScript.
How is the browser's own line breaking measured for comparison?
Each word is wrapped in an invisible `<span>` element and its `y` position is read back, so a change in `y` marks the start of a new line. Ratios come from comparing the width of each line under `text-align: justify` with the same line under `text-align: left`, divided by the assumed stretchability.
Can typeset fix the line breaks a browser already made?
Yes. The algorithm can be used to correct browser line breaks by adjusting the CSS `word-spacing` property per line. Webkit based browsers do not support sub-pixel word-spacing, so the alternatives are absolute positioning each word or splitting lines into segments with integer word spacing, as the Flatland example does.
Does typeset support anything other than justified text?
Yes. With a suitable selection of boxes, glue and penalties it can do ragged right and centred setting, and varying line widths let text flow around illustrations, asides and quotes, or form non-rectangular shapes such as triangles and circles.
Is typeset available as an npm package?
The repository has no GitHub releases and no package manifest or install instructions. The source lives in `src/` and `lib/` with examples under `examples/`, so it is vendored into a project rather than installed as a dependency. The licence is BSD-2-Clause and the last push was on 2026-06-20.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/bramstein-typeset)Was this useful?