Thousands of tesserae laid in rows that follow every outline, as a Roman mosaicist would.
This follows Alejo Hausner's simulated decorative mosaics. The figure and its guilloche border are painted as a label map, and two exact Euclidean distance transforms are taken from the label edges: one colors each region in rows that follow its outline, from a dark outline row inward, and one, which also counts the panel border and any guide curves, gives the direction field whose gradient is the edge normal (smoothed as four times the angle, since a square repeats every quarter turn). Seeds are dropped along the iso-contours of that distance, nearest rows first, then relaxed as a centroidal Voronoi tessellation under an oriented max-norm, where each cell only takes grid points of its own region. Each tessera is cut to its cell's extent in its own frame, so stones shrink where they meet an edge, and they are laid outlines first over the red sinopia underdrawing.
Try it. Paint a curve anywhere to bend the direction field: new rows grow parallel to it and the tiles flow into place. Choose a dolphin, fish, partridge or rosette, switch between fine, medium and coarse tesserae, and clear your guides to let the rows settle back.
Paste this into Claude Code, Codex or any coding agent to get a simple version running, then take it wherever you like.
Build a Roman floor mosaic generator with JavaScript and the HTML canvas element, where square tiles line up in rows that follow the outlines of a figure. Put everything in a single index.html file with no libraries or build step, so I can open it directly in a browser.
Start simple:
- Make a canvas that fills the window, stays sharp on high-DPI screens (scale by devicePixelRatio), and resizes with the window.
- Draw a simple figure (a fish made of an ellipse and a triangle tail) in flat colors onto a hidden canvas the same size, and read it back with getImageData. That is the picture the mosaic will copy.
- Find its edges (pixels whose color differs from a neighbor) and compute, for every pixel of a coarse grid, the distance to the nearest edge. A simple two-pass chamfer distance transform is fine.
- Place tiles in rows: for row k, take the grid points whose distance is close to (k + 0.5) times the tile size, and accept a point only if no tile already sits within 0.9 tile sizes of it.
- Turn each tile to match the edge: the gradient of the distance field points away from the nearest edge, so use its angle as the tile's rotation. Draw each tile as a slightly smaller rotated square in the picture's color at its center, on a light gray grout background.
Once that works, make it beautiful:
- Give the stones a Roman palette (white marble, ochre, terracotta, slate, black) with a little random variation per tile, and a bevel: a light stroke on the edges that face the upper left and a dark one on the others.
- Shade the figure in bands that follow its outline: a dark first row, then lighter rows inward.
- Animate the laying, closest rows to the edges first.
Explain the key ideas in short code comments. When you're done, tell me how to open it and suggest three directions I could take it next, such as relaxing the tiles with a centroidal Voronoi tessellation, letting the user paint guide curves that the rows follow, or a twisted guilloche border.