Chapter 06 · Part II · Rules that build shapes

Fractal flames

Take the chaos game from Chapter 4, bend its maps so straight lines curl, count where a single wandering point lands instead of painting it, and show the counts on a log scale colored by the route the point took. What comes out looks like lit smoke or folded silk. This chapter takes the recipe apart, one step at a time.

1992the year Scott Draves created the flame algorithm, later written up with Erik Reckase
20iterations thrown away before plotting starts, so the point has settled onto the attractor
½how far each jump moves the point's color toward the color of the map that was chosen

Common mix-up: nothing in a flame is drawn as a line or a shape. The curves and veils are piles of landings: one point hops millions of times, each pixel counts how often it was hit, and the brightness you see is the logarithm of that count. Bright means visited often, not drawn thickly.

Where flames come from

From the chaos game to flames

Chapter 4 built shapes out of a handful of maps. Each map is a rule that takes any point in the plane and moves it somewhere else, usually closer to some spot and a bit rotated or squashed. A set of such maps is an iterated function system, or IFS. John Hutchinson showed in 1981 that every IFS made of shrinking maps has exactly one shape that it leaves unchanged: apply all the maps to that shape, gather up the copies, and you get the shape back. That fixed shape is the IFS's attractor. Michael Barnsley's Fractals Everywhere (1988) made the idea famous with a fern drawn by four maps.

The quickest way to draw an attractor is the chaos game. Start anywhere, pick a map at random, apply it, mark the spot, and repeat. After a few steps the point is so close to the attractor that you can't tell the difference, and from then on every mark lands on it. Run the game long enough and the marks fill the shape in.

Classic IFS maps are affine. An affine map can scale, rotate, shear, flip and shift, but it can't bend: straight lines stay straight and parallel lines stay parallel. That's why IFS pictures look like triangles made of triangles, or ferns made of ferns. They are crisp and convincing, and a little mechanical.

In 1992 Scott Draves changed three things about the chaos game, and the result is the fractal flame. He and Erik Reckase later described the method in a paper, "The Fractal Flame Algorithm" (2003, revised 2008), and Draves published the software that implements it, flam3, as open source.

  • Bend the maps. After each affine map, the point goes through a non-linear function, a variation, that curls, folds or inverts the plane.
  • Count instead of painting. Each landing adds one to a counter for that pixel. The picture is made from the counts at the end, shown on a log scale.
  • Color by history. The point carries a color number that drifts toward the color of each map it passes through, so a pixel's color records how the point got there.

Each change fixes a specific problem, and each one is easy to see on its own. The instruments below let you switch them on and off.

Change one

Variations: bending the maps

A flame's map does two things in a row. First the usual affine step: multiply the point by a small table of six numbers to stretch, rotate and shift it. Then the bend: feed the result into a variation, a fixed formula that moves points around in a curved way. Draves's paper catalogues dozens of them. Most are built from two quantities you can read straight off any point: r, its distance from the center, and an angle, θ.

Some variations are gentle. Sinusoidal runs each coordinate through a sine, which squeezes the whole plane into a square and wraps the far parts back on themselves. Swirl rotates every point by an angle that grows with its distance from the center, so the plane twists like stirred paint. Some are drastic. Spherical divides by the square of the distance, turning the plane inside out: points near the center are flung far away, and far points crowd in toward it. Polar unrolls the plane into a strip, using the angle as one coordinate and the distance as the other. Julia takes a square root, so each point has two possible answers, and the variation picks one of them at random.

Each map in a flame can blend several variations with weights: half swirl and half linear, say. The full map looks like this.

Fᵢ(x, y) = Σⱼ vᵢⱼ · Vⱼ(aᵢx + bᵢy + cᵢ, dᵢx + eᵢy + fᵢ)
Map number i first moves the point with its six affine numbers a to f, then passes the result through each variation Vⱼ and adds the outputs together, each scaled by a blending weight vᵢⱼ. With only the linear variation, this is an ordinary IFS map.

The variation doesn't need to shrink anything. A plain IFS needs every map to be contracting, or the chaos game flies off to infinity. A flame drops that guarantee. Some combinations of maps and variations still settle down onto a beautiful attractor; some spray points across the whole plane; some collapse onto a single dot. Choosing good parameters is part craft and part search, which is why flame software leans so heavily on random suggestions and on breeding (see Electric Sheep, below).

VariationFormulaWhat it does to the plane
linear(x, y)Nothing. The plain IFS map.
sinusoidal(sin x, sin y)Squeezes everything into a square from −1 to 1, folding the outside back in.
spherical(x, y) / r²Turns the plane inside out around the unit circle. Near becomes far.
swirl(x sin r² − y cos r², x cos r² + y sin r²)Rotates each point by an angle that grows with the square of its distance.
horseshoe((x − y)(x + y), 2xy) / rDoubles every angle while keeping the distance: the plane wraps around twice.
polar(θ / π, r − 1)Unrolls circles into horizontal lines and rays into vertical ones.
handkerchiefr (sin(θ + r), cos(θ − r))Billows the plane like cloth, with folds that shift with distance.
heartr (sin(θr), −cos(θr))Squeezes each circle's angle by its radius; the unit disc becomes a heart.
disc(θ / π)(sin πr, cos πr)Swaps the roles of angle and distance, spinning rings into a disc.
spiral(cos θ + sin r, sin θ − cos r) / rWinds the plane into an inward spiral.
hyperbolic(sin θ / r, r cos θ)Bends lines into hyperbola-like arcs.
diamond(sin θ cos r, cos θ sin r)Folds the plane into a diamond lattice of ripples.
julia√r (cos(θ/2 + Ω), sin(θ/2 + Ω))A complex square root; Ω is 0 or π at random, so each point has two images.
Go deeper: the paper's odd angle, and post and final transforms

The variation formulas in Draves and Reckase's paper use r = √(x² + y²) as you'd expect, but define the angle as θ = arctan(x / y), measured from the vertical axis rather than the horizontal one. A second angle, φ = arctan(y / x), is the usual one. The difference is just a rotation and a mirror, but it changes how each variation looks, so the formulas on this page and the instruments below use the paper's θ to match flam3's pictures.

Horseshoe is the easiest to decode. Write the point as r(cos α, sin α) with the usual angle α. Then (x − y)(x + y) / r = r cos 2α and 2xy / r = r sin 2α. Same distance, double the angle. That's exactly what squaring a complex number does to its angle, which is the subject of the next chapter.

Spherical is inversion in the unit circle: a point at distance r goes to distance 1/r in the same direction. Circles map to circles (or lines), which is why spherical flames are full of round, nested shapes.

The paper adds two refinements. A post transform is a second affine map applied after the variations, so a map can be "affine, bend, affine". A final transform is one extra map applied to every point just before it's plotted, without feeding back into the game. It's a lens over the whole picture: you can wrap an entire flame onto a sphere or a swirl without changing the attractor underneath.

Instrument 1

The variation lab

A square grid of points from −2 to 2, pushed through one variation. The faint grid is where the points start; the colored lines are where they end up. Slide the amount from 0 (linear, nothing moves) to 1 (the full variation) to watch the plane bend. Drag the white probe to follow a single point.

Formula
Probe in–
r and θ–
Probe out–

Horizontal lines are drawn in blue, vertical lines in pink, so you can see which way each one went. Julia has two answers for every point; its second branch is drawn dashed.

Change two

Count, don't paint

The ordinary chaos game marks each landing with a dot. If the spot already has a dot, nothing changes. That throws away the most interesting information in the game: how often the point visits each place. Some parts of an attractor are visited constantly, others rarely, and the difference is what gives a flame its depth.

So a flame renderer keeps a histogram, a grid of counters, one per pixel. The game runs exactly as before: pick a map at random according to its weight, apply its affine step and its variations, and get a new point. The first 20 points are thrown away, because the starting point was arbitrary and the game needs a few moves to land on the attractor. After that, every new point adds one to the counter under it. The paper's renderer also adds the point's color to a running total for that pixel; more on that in a moment.

Weights matter more here than in a plain IFS. In Barnsley's fern, the weights only change how quickly each part fills in. In a flame, they change the final brightness of every region, because brightness is visit frequency. Turn one map's weight up and everything it produces gets brighter, along with everything downstream of it.

A good flame needs a lot of samples. A screen-sized image has a couple of million pixels, and the faint parts of the attractor need a few hits each to look smooth rather than speckled. Renderers commonly plot tens or hundreds of points per pixel for a finished image. That is hundreds of millions of iterations for one frame, which is why flame rendering was long a batch job, and why the instrument below fills in progressively rather than all at once.

Go deeper: why the counts span such a huge range

The chaos game is a random walk that keeps returning to the same neighborhoods. Where maps overlap, or where a variation squeezes a large area into a small one, the landings pile up. Spherical, for example, takes the entire outside of the unit circle and packs it inside; every point that lands far away comes back to a crowded spot near the center.

The result is a very lopsided histogram. A few pixels collect thousands of hits while large regions get one or two, and the ratio grows as the render runs longer. The renderer instrument reports the busiest pixel and the average lit pixel as it goes; watch the gap widen.

Mathematically, the counts approximate a measure on the attractor, the invariant measure of the random process. Two flames can have exactly the same attractor as a set and look completely different, because their measures differ. The weights decide the measure; the maps decide the set.

Turning counts into light

Log density and gamma

Now the counts have to become brightness. The obvious way is linear: the busiest pixel is white, a pixel with half as many hits is half as bright, and so on. Try it in the renderer below with the display set to linear. The picture is almost black, with a few bright knots. Because the busiest pixels have thousands of times more hits than typical ones, a linear scale spends all its brightness on the top of the range.

Draves's fix is to show the logarithm of the count. A logarithm turns ratios into steps: 10 hits, 100 hits and 1,000 hits become three evenly spaced levels of brightness instead of the last one drowning out the other two. Your eye already works roughly this way. That's why the paper calls it log-density display, and why flames look like glowing gas instead of a few hot spots.

brightness = ( log(1 + n) ÷ log(1 + nₘₐₓ) )^(1/γ)
n is the count in one pixel and nₘₐₓ is the count in the busiest one. The log squeezes the huge range of counts into 0 to 1; raising to the power 1/γ then lifts the dim end so faint structure shows. The renderer below uses exactly this formula.

The second step is gamma. Screens don't emit light in proportion to the number in a pixel; a value of one half produces much less than half the light. Gamma correction compensates by raising brightness to a power 1/γ, which lifts the darker values. The usual γ for screens is about 2.2. Flames often look best with more, around 3 or 4, because a stronger lift pulls even more of the faint wisps out of the dark; try it in the renderer. Too much, and the background turns gray and noisy, because every stray landing becomes visible.

The renderer also has a brightness control, which just scales everything before gamma. Values above 1 push more pixels to full white and make the core look hotter.

Go deeper: the paper's version, and vibrancy

The paper keeps four numbers per pixel: summed red, green and blue, plus a count it calls alpha. Its log-density step scales each pixel's summed color by log(α) / α. Since the summed color divided by α is the average color, that is the same as multiplying the average color by log α: the hue comes from the average and the brightness from the log of the count. The formula above is the same idea in a normalized form, with 1 added inside the log so a single hit doesn't come out as zero.

Gamma can be applied in two places. Applied to each color channel separately, it brightens dim regions but washes out their color, because it lifts the weaker channels the most. Applied to the alpha only and then used to scale the color, it keeps hues saturated. flam3 has a parameter called vibrancy that blends between the two. This page's renderer applies gamma to the brightness only, which is the fully vibrant end.

Instrument 2

A live flame renderer

A real flame renderer, running in your browser. Choose 2 to 4 maps, give each a variation and a weight, and watch the histogram fill in. It runs a few milliseconds of chaos game every frame, then redraws the log-density image a few times a second, so the picture sharpens as you watch. Display controls (gamma, brightness, log or linear) recolor the existing histogram instantly; anything that changes the attractor starts a fresh one.

Maps:
Points plotted0
Hits per lit pixel–
Busiest pixel–
Status–

Try Display: linear to see why the log matters, then Color: last map only to see what color by history adds. Some random shapes spray points everywhere or collapse to a dot; that is a real property of flames, not a bug. Press New random shapes again.

Change three

Color by history

The simplest way to color an IFS is by map: give each map a color and paint each landing the color of the map that put it there. It works, but it's flat. Each map's copy of the attractor comes out as one solid hue, and the picture looks like a stencil.

Flames do something subtler. Each map gets a color coordinate, a number between 0 and 1 that points into a palette, a gradient of 256 colors. The moving point carries a color coordinate too. Each time a map is applied, the point's coordinate moves halfway toward that map's coordinate.

c ← (c + cᵢ) ÷ 2
c is the point's color coordinate and cᵢ is the color coordinate of the map just chosen. The point's color always sits halfway between where it was and where the latest map wants it.

Unroll that rule and it says something lovely. After a run of jumps, the latest map contributes one half of the point's color, the map before it one quarter, the one before that one eighth, and so on, halving each time. The color is a fading memory of the route. Two landings in the same pixel that arrived by the same recent sequence of maps get the same hue; landings that arrived another way get a different one.

That route is exactly what separates one part of a self-similar shape from another. In the Sierpinski triangle, the sequence "top, left, left" leads to one particular small sub-triangle. Color by history gives each sub-copy, at every scale, its own shade, blended smoothly with its neighbors. Each pixel's final color is the average color of all its landings, and its brightness comes from the log of their count, so color and density are independent: you can recolor a flame completely without changing its shape.

In the renderer, switch Color between history and last map only. The shapes stay put; the shading inside them changes from smooth, layered gradients to flat patches.

Go deeper: the color coordinate is a binary address

Take a flame with two maps, colors 0 and 1. Each jump halves the coordinate and adds either 0 or ½. After many jumps, the coordinate written in binary is 0.b₁b₂b₃…, where b₁ is the most recent map, b₂ the one before, and so on. The color coordinate literally is the address of the point's recent history, read as a number.

With more maps, or colors that aren't 0 and 1, the digits blur together, but the principle holds: the most recent choice dominates, and older choices fade geometrically. Later versions of flam3 and the editors built on it let each map move the coordinate by a different fraction than one half, often called color speed, which changes how long the memory lasts.

Symmetry maps, described next, are usually set not to touch the color at all. That way each rotated copy of the flame gets exactly the same coloring as the original.

Finishing touches

Symmetry, supersampling and density estimation

Symmetry. A rotation by a sixth of a turn is just another map. Add it to the list, and every piece of the attractor gets a copy rotated by 60°, then 120°, and so on, because the game can apply the rotation any number of times. The attractor becomes six-fold symmetric. Add a mirror reflection and it becomes symmetric like a snowflake. Because these maps leave the color coordinate alone, every copy matches. The renderer above has a symmetry row; it applies a random rotation from the set after every jump, which gives the same attractor.

Supersampling. If the histogram has exactly one counter per pixel, edges come out jagged: a thin bright filament either lands in a pixel or it doesn't. The fix is to make the histogram larger than the image, say three times as wide and three times as tall, and then average each 3 × 3 block down into one output pixel. That's supersampling, a standard way to remove jagged edges, and the paper's renderer does it with a smooth filter rather than a plain average.

Density estimation. Even with millions of samples, the faint parts of a flame are speckled: a pixel with one hit sits next to a pixel with none. Blurring the whole image would fix the speckle but smear the sharp filaments too. The paper's answer is a blur whose width depends on the local density. Where the counts are low, each hit is spread over a wide area; where counts are high, the blur shrinks to nothing and detail stays crisp. Statisticians call this kind of thing adaptive kernel density estimation. Turn it off in the renderer and look at the outer wisps: they turn to dust.

StepProblem it fixesCost
VariationsIFS pictures look rigid, made of straight-edged copiesA few trig functions per iteration
Histogram + logDots throw away visit frequency; linear brightness hides all but the hottest spotsOne counter (and color sum) per pixel
GammaFaint structure is invisible on a screenOne power per pixel at the end
Color by historyColor by map looks flat, like a stencilOne multiply-add per iteration
SymmetryHand-designing symmetric flames is hardExtra maps; free at render time
SupersamplingJagged edges on thin filamentsA bigger histogram, more samples
Density estimationSpeckled noise in sparse regionsA variable blur at the end
Go deeper: how the renderer on this page cuts corners

The renderer above is faithful in the parts that matter: real variations, a real histogram of counts and color sums, the log formula and gamma shown earlier, color by history, rotational symmetry, and a variable-width density estimate. To stay smooth on a phone, it simplifies two things.

  • No supersampling. The histogram is one counter per screen pixel, capped at about 0.8 million counters, so very fine filaments can look slightly jagged.
  • A coarse density estimate. Pixels with one hit spread it over a 5 × 5 square; pixels with two or three hits spread over 3 × 3; denser pixels are left alone. The paper's filter varies smoothly with density.

It also stops after about 40 hits per pixel on average, so it doesn't keep your processor busy once the picture has stopped changing.

Flames that breed

Electric Sheep

A flame is defined by a short list of numbers: for each map, six affine coefficients, a weight, a color coordinate and some variation weights, plus a palette and a few display settings. Draves called that list a genome. Change the numbers smoothly over time and the flame moves smoothly, so a sequence of genomes is an animation.

In 1999 Draves started Electric Sheep, a screensaver that turns idle computers into a distributed flame renderer. Each animated flame is a "sheep". Computers running the screensaver download genomes, render frames, and upload them to a server, which assembles the frames into animations and sends them back out. Everyone watching sees the same flock.

The audience also chooses. Viewers can vote for sheep they like. The server then breeds new genomes from the popular ones, mixing maps from two parents and mutating some numbers, which is a genetic algorithm with human taste as the fitness function. Draves wrote it up in 2005 as a case study in "aesthetic evolution". The variation library grew the same way: when people added new variations to the open-source code, they became new genes for the flock to explore.

Flames spread well beyond the screensaver. Free editors such as Apophysis and JWildfire made flame design a hobby of its own, with variation libraries far longer than the paper's. JellyDazzle's flame IFS mode is a live, audio-driven descendant of the same idea.

Go deeper: animating without flicker

Animating a flame isn't just rendering many frames. A flame rendered with a finite number of samples has random noise, and if each frame's noise is independent, the animation shimmers. The paper's renderer handles this with temporal filtering: each frame is built from samples taken at several moments spread across the frame's time slot, which acts as motion blur and smooths the noise between frames.

The last scene of the walkthrough at the top of this page uses a cheap version: it never clears the histogram, but fades every count a little each frame before adding new samples, so each frame is a blend of the last few.

Cheat sheet

Terms from this chapter

IFS
Iterated function system: a set of maps whose combined fixed shape, the attractor, is the fractal.
Affine map
A map that scales, rotates, shears and shifts, but keeps straight lines straight. Six numbers, a to f.
Variation
A fixed non-linear function applied after a flame map's affine step: swirl, spherical, polar and dozens more.
Weight
How often a map is chosen in the chaos game. In a flame it sets brightness, not just fill speed.
Histogram
A grid of counters, one per pixel (or more), recording how often the point landed there.
Log density
Showing the logarithm of each pixel's count, so that huge differences in visit frequency become gentle steps of brightness.
Gamma
Raising brightness to the power 1/γ to lift faint values. About 2.2 matches a screen; flames often use more.
Color coordinate
A number from 0 to 1 carried by the point and moved halfway toward each map's own coordinate at every jump.
Supersampling
Rendering a larger histogram than the image and filtering it down, to smooth jagged edges.
Density estimation
A blur whose width shrinks as the local count grows: removes speckle in sparse areas, keeps dense detail sharp.
Genome
The full list of numbers that defines one flame.
Electric Sheep
Draves's distributed screensaver, started in 1999, that renders, animates and breeds flames.
Where this comes from

Sources