Palette morphing between waypoints
A zoom movie whose colouring is set per waypoint, with the palette and the cycle density interpolated across every camera move.
7 minutes read
1352 words
Contents6
Each waypoint of a movie carries its own palette and its own colour cycle density, and the renderer interpolates both across the move between them.
A zoom movie is a list of waypoints and the camera moves between them. Each waypoint carries the two settings that decide colour. One is the palette the frame is drawn in. The other is how many colour cycles the picture spans. The renderer interpolates both across every move, on the same eased parameter that drives the zoom. A frame at a stopped waypoint is drawn exactly as that waypoint defines it. Every frame between two waypoints is part of the way from one setting to the other.
Escape time and the palette coordinate
Escape time assigns each pixel the number of iterations its orbit takes to leave the escape radius. The definition and the iteration are given in the anatomy of the set . That count is an integer, so colouring from it directly produces visible bands at each integer step.
The smooth escape count removes the banding. Write for the escape radius, for the integer count at which first exceeds it, and for the magnitude reached at that step. The smooth count is
and it varies continuously across the plane.1 A point that lands exactly on the escape radius gives , so the logarithm is zero and . A point that overshoots to gives , which is correct because such a point had already escaped one iteration earlier.
This renderer uses . A large escape radius makes the correction term small. The fractional part of then follows the magnitude reached, not the distance one iteration happened to carry the orbit past the radius.
A palette is a function from a coordinate in to a colour, cyclic at its ends. The renderer maps to with
where is the fractional part, is a constant offset, and is the quantity this page calls the cycle density.
Cycle density
Equation (2) gives
as the number of complete palette cycles per unit of
. It is the colourScale field of a waypoint, and the setting the renderer’s toolbar labels Colour.
The square root in equation (2) is what makes a usable control. Escape counts near the boundary grow without bound, so would run through the palette faster and faster without it. Taking the square root slows that growth, and then sets the rate.
Raising puts more colour cycles across the same picture. Lowering it spreads one cycle over a wider range of escape counts. At a shallow view the choice is aesthetic. At depth the escape count between two adjacent pixels can differ by enough that a high sends them to unrelated parts of the palette.
The table below measures that. One waypoint at a view height of , 5,000,000 iterations, rendered at 640 by 360 in a single palette with only changed. The delta column is the mean absolute difference between horizontally adjacent pixels, taken over the largest of the three channels, on a scale of 0 to 255. The rough column is the share of adjacent pairs differing by more than 8.
| Cycle density | Mean adjacent delta | Rough | Distinct colours |
|---|---|---|---|
| 0.10 | 6.57 | 22.7% | 372 |
| 0.01 | 1.19 | 1.4% | 355 |
| 0.002 | 0.44 | 0.4% | 329 |
| 0.0005 | 0.22 | 0.2% | 184 |
At more than a fifth of adjacent pixel pairs jump by more than 8 of 255. That reads as speckle. At the share is 1.4 percent, and 355 of the 372 distinct colours survive. Below the picture starts losing colours. At the distinct-colour count has halved, because the image no longer spans enough of one cycle to show the palette.
The useful range therefore depends on depth, which is the reason belongs to the waypoint rather than to the film.
Palettes as tables
A palette is baked to a lookup table of 1024 entries in sRGB, and sampled between entries by linear interpolation. Three kinds of palette bake to the same table. One is defined by a cosine formula, one by a list of colour stops, and one is generated from a seed. The blend operates on the tables, so any two of them pair in any combination.
Enlarging the table does not reduce the speckle described above. Rebuilding with 8192 entries and re-rendering the same two views leaves no byte differing by more than 1. The mean adjacent delta is unchanged to two decimals. The table is already sampled between entries, so further entries interpolate a function that was smooth. The speckle comes from the escape count varying quickly between adjacent pixels, and is the control over it.
Interpolating between waypoints
One eased parameter in runs along each camera move. It drives the zoom, the pan, the iteration ceiling, the cycle density and the palette blend together. A second, separately eased parameter would let colour lead or lag the camera, which a viewer reads as an effect of its own.
The cycle density interpolates geometrically. For densities and at the two waypoints,
Equation (3) is a straight line in , so equal fractions of the move multiply the density by equal factors. Band density is perceived multiplicatively, so a linear ramp from 0.2 to 1.6 would spend most of the move in the dense half. The iteration ceiling interpolates the same way and for the same reason.
The palette interpolates on the baked tables, entry by entry, in Oklab. The transform and the measurements behind that choice are on the colour spaces page . Blending the stored bytes darkens the middle of a blend, measured at 61 percent of the luminance the two ends average to. Oklab holds the apparent rate of change constant instead, which is what a viewer watching a blend over several seconds is judging.
At and the table is copied, not blended. A held waypoint is therefore byte-identical to the same waypoint rendered as a single image. A film whose waypoints all name one palette produces the frames it produced before any of this existed.
Example film
The film below has four waypoints, each in a different palette. It travels from the neck of the main cardioid to a point on the boundary and back.
| Waypoint | Palette | Cycle density | Iterations | Centre | View height |
|---|---|---|---|---|---|
| 0 | ember |
0.0563 | 10,000 | ||
| 1 | fire |
0.0496 | 10,000 | ||
| 2 | ultraviolet |
0.0629 | 100,000 | ||
| 3 | random:b427b313 |
0.0704 | 10,000 |
Waypoint 3 returns to the coordinates of waypoint 0 and differs only in its palette. That palette is generated from the seed b427b313, so it is in no built-in list. Waypoint 2 is 914,600 times narrower than waypoint 0.
Each waypoint is held for 3 seconds. The film runs 8026 frames at 60 frames per second, 133.76 seconds, at 3840 by 2160. Of those frames 6772 are drawn. One leg plays in reverse and two clips repeat, and a repeated frame is encoded from the one it repeats.
Mandelbrot --movie COLOR.fmovie --res 3840x2160 --out color.mp4 \
--speed 0.5 --ramp 5 --progress

The move from waypoint 0 to waypoint 1 runs 27.46 seconds. The strip below samples it, and its two ends are the held frames.

References
-
Renormalizing the Mandelbrot escape. Linas Vepstas. Retrieved 8 September 2026. https://linas.org/art-gallery/escape/escape.html (opens in a new tab) ↩︎