View beier-neely.js
// Core math for computing displacement of a pixel per Beier-Neely algorithm:
function computeDisplacement(p1, q1, p2, q2, x2) {
const len1 = distanceBetween(p1, q1),
len2 = distanceBetween(p2, q2),
u = dot(diff(x2, p2), diff(q2, p2)) / (len2 * len2),
v = dot(diff(x2, p2), perpendicular(diff(q2, p2))) / len2,
A = 10,
B = 1,
View .block
height: 600

Trying out the scribble fill method from this old Apple patent by John B. Turner with a bit of spline embellishment at the corners. Seems to work pretty well for more convex shapes, but results can get weird otherwise (the Olympic Peninsula seems especially uncooperative).

Some possible tweaks:

  • Drop any orphan scribbles with only two or three vertices
  • Add some random control points to make the scribble segments a little swoopier
  • Add a hand-drawn effect with SVG turbulence/displacement filters
  • Select intersections based on a more simplified outline of the shape
View .block
height: 600

Playing around with Anders Hoff's sand spline technique. Actually drawing enough iterations for a spiral this long takes pretty much forever because of all the getPointAtLength() calls, there's probably a way around that.


Comparing two different approaches to shape transformation:

The top is the interpolation technique in this demo: add points until both shapes have an equal number and then pick the optimal winding order by the sum of squared distances.

The bottom is inspired by Procrustes superimposition, which decomposes the transform necessary to align two shapes into scale, translation, and rotation components. By interpolating those three components separately, it can produce nicer transitions in certain cases (e.g. in the extreme case, a shape turning into a rotated version of itself). But since it's meant for aligning similar shapes with meaningful point-to-point correspondences, it seems to do more harm than good in most cases with two unrelated shapes, though evenly spacing the points around each ring helps.

See also: [Smoother polygon transitions](

View .block
height: 400
View HI.json