Signal and Sensation

Soft Edges

Six blobs merging and splitting, their outline pulled out of a grid of numbers by a sixteen-case lookup table.

Open fullscreen →

What it is

Blobs drift around and fuse when they meet. Drag one. Then make the grid coarser and watch the smooth curve become a jagged polygon, because the curve was never there. It’s inferred, cell by cell, from a lattice of numbers.

How it works

Every point in the square has a value, the sum over blobs of r²/d². That formula is chosen so a lone blob’s own radius is exactly where the value equals one, which makes 1 the natural threshold. Where blobs overlap their values add, so the outline bulges out to meet. The whole metaball effect is addition.

Marching squares turns that field into lines. For each grid cell, classify its four corners as above or below the threshold. Four bits, sixteen possible patterns, and a table saying which cell edges the curve crosses for each.

Then interpolate along those edges. If one corner reads 0.9 and the next reads 1.1, the crossing sits halfway. That interpolation is the entire reason the output looks smooth on a coarse grid rather than blocky.

Two of the sixteen cases are genuinely ambiguous, with diagonal corners inside and the other two out. The corners alone can’t say whether the lobes join or pass, and this implementation takes the “separate” reading consistently, which is the usual choice.

What surprised me

The test I expected to be decorative turned out to be the one that would have caught a mistake in the table.

Contouring a single blob should recover a circle, so I asserted the measured perimeter approaches 2πr, and at a 200×200 grid it lands within 1%. Fine.

The useful test was a different one. Every endpoint of a closed contour has to be shared by exactly two segments. Get one entry in the sixteen-case table wrong and the outline still looks broadly plausible while quietly having loose ends, and that test fails immediately and unambiguously.

I also wrote a test asserting each case only names edges that genuinely straddle the threshold for that corner pattern, derived from the bit pattern rather than copied from the table, which checks the table against its own definition rather than against my typing.

What I would do next

Marching cubes, and the same lookup-table trick in three dimensions, where there are 256 cases and rather more ambiguity.