Mosaic

The same files, the same rectangle, three packing algorithms. Every tile is a real file in projects/playground: builds and the checkers that guard them, sized by what is actually on disk. The numbers under each picture are the aspect ratio of every tile it produced, which is the only thing these three algorithms disagree about.

size by
group by
include
labels

What the three of them actually do

Slice and dice cuts the rectangle on one axis, flips the axis at every level of nesting, and gives each item a slice proportional to its size. It is one line of arithmetic, it keeps your input order perfectly, and with a hundred items in a group it produces tiles you cannot see, let alone click. Every bad treemap you have ever met was this one.

Strip keeps the order too, but packs full width rows instead of slices: it keeps adding items to the current row while the row's average aspect ratio improves, then closes the row and starts another underneath. Order preserved, ratios much better, and a distinctive failure where one huge item forces a nearly empty row.

Squarified sorts descending first, then walks the remaining free rectangle. It lays each row along the rectangle's short side, and it keeps adding to that row while worst() improves, where worst() is the worst aspect ratio in the row given the side length it sits on. When adding the next item would make the worst tile worse, it closes the row, subtracts it from the free rectangle, and starts again on whatever is left.

The part that is easy to get wrong

The short side is a property of the remaining rectangle, not of the original one, and it has to be recomputed every time a row closes. Hoisting it out of the loop looks like an obvious optimisation and it is a bug: it only shows up when one group dominates the data, which is exactly the shape this dataset has. Measured here, it moves a tile by up to 437 pixels, and takes the median tile from 1.11:1 to 2.38:1 and the worst from 1.95:1 to 107.92:1.

I was sure there was a second trap in the same loop, and there is not, which is the more useful half of this build. The scale factor that turns a value into an area is free area over remaining value, and both of those numbers change every time a row closes, so recomputing it per row feels obviously necessary. It is not. Closing a row removes exactly its own area and exactly its own value, so the ratio is unchanged: the scale is invariant, and computing it once at the top gives the identical layout to 5.6e-12, which is floating point noise. That variant is still in the code behind a flag, and _verify/check_mosaic.js asserts that it changes nothing. A confident argument about a bug that the arithmetic does not have.

What it buys and what it costs

Squarified wins the ratio statistics under every setting on this page, and it is the only one of the three whose tiles you can reliably hover. It pays for that by destroying order: the tiles are sorted by size, so nothing about their position tells you anything about when a build was made. Switch group by to month and read the strip layout instead if you want the nights in sequence. There is no arrangement that gives you both, and knowing which one you are holding is most of the skill.

Where the worst tile actually comes from

Squarified's worst tile on this page is , and over one flat rectangle with no nesting at all the same algorithm never goes past . I assumed the difference was inherited: a small file trapped inside a lane rectangle that was already a sliver. It is not. Every one of the ten worst tiles is a build's README.md, and every one of their parent rectangles is between 1.0:1 and 2.0:1. The mechanism is the spread of sizes inside a group, not the shape around it. A build folder holds a page of well over a thousand lines and a readme of about a dozen, and the last and smallest item gets whatever is left once everything bigger has been packed well. Turn on builds only and the worst tile improves immediately, because that removes the lopsidedness rather than changing the algorithm.

One practical consequence, measured rather than argued: at this size squarified produces tiles thinner than a pixel, strip produces , and slice and dice produces . A tile below a pixel is not a tile. It cannot be seen, hovered or clicked, and the browser's own geometry rounding is larger than the thing it is rounding.

And what it says about this folder

Data measured from disk on 2026-09-23 by _verify/mosaic_dataset.py. Lanes come from the genre column of PLAYTIME_LOG.md by a left to right keyword scan, so a build tagged "game, physics" counts as a game and "toy / simulation sandbox" counts as simulation. Nothing on this page is estimated or rounded from memory.