The portrait on this site starts with 11,885 points sampled from my photograph. The compact function below the image projects those points. It does not contain a compressed drawing of my face.

That distinction matters when talking about a “190-byte portrait”. The number describes the projection function. The photograph-derived points and the renderer are separate.

Likeness comes from the data

Each input point has coordinates and an ink weight. Together, those points carry the recognizable features. Changing the projection moves them; replacing the point data would change the person.

This gives the animation a stable place to return to. Even when the points spread across the viewport, each one still has a position in the portrait.

The kernel makes a small set of changes

The projection rotates the horizontal and depth coordinates with sine and cosine. Time adds a slight breathing motion and small waves. The third output adjusts the point's ink weight according to its depth.

The renderer then places the result in the page and draws it. Keeping the function independent of the canvas makes it possible to inspect the math without navigating event handlers, layout measurements and animation timing.

Choreography is another decision

The entrance forms the portrait locally over 2.4 seconds. Later, those same points become a swimming manta ray, follow a slow circuit beneath the text and return to the portrait. Travel continues through both transformations. The points change their local shape around a moving centre, so forming the portrait does not stop its return journey.

Hovering over the portrait starts the transformation while it is already drifting. An oblique camera projects the animal in three dimensions. Nearer points grow slightly larger and darker as it banks through a broad turn. The fins deform in depth, and the tapered tail follows a chain of links with inertia. A nearby pointer displaces the points by at most 3.2 pixels, then they settle back. The lower arc can carry a fin or the tail beyond the viewport. Scrolling reveals that part of the path; the animal keeps its position in the page. Placement and timing belong to the renderer, which knows the page and window dimensions.

The distinction keeps changes local: adjusting the entrance does not require changing the face data or rewriting the projection.

The page still needs to be readable

Motion is the main visual element here, but it should not become a prerequisite for reading. Text appears independently. With reduced motion enabled, the page draws a still portrait and turns off autoplay.

I can tune the path without changing the portrait data. The projection stays small enough to read in one sitting.

Look at the code

Projection and renderer ↗The entrance choreography ↗The swimming form and path ↗