Fractals

Mandelbrot, Julia sets, and escape-time rendering

Some shapes contain infinite detail. No matter how far you zoom, new structures emerge. These are fractals: patterns that repeat at every scale, revealing complexity from simplicity.

The mathematics is deceptively simple. Take a number, square it, add a constant, and repeat. Whether this process spirals off to infinity or stays bounded determines whether a point belongs to the fractal. The boundary between these two behaviors is where the magic lives.

The Mandelbrot Set

The Mandelbrot set is perhaps the most famous fractal. For each point cc in the complex plane, we iterate:

zn+1=zn2+cz_{n+1} = z_n^2 + c

Starting with z0=0z_0 = 0, we ask: does this sequence escape to infinity, or does it stay bounded?

Points where the sequence stays bounded form the Mandelbrot set, typically colored black. Points that escape are colored based on how quickly they escape. The boundary between escape and capture is infinitely complex.

vec2 complexSquare(vec2 z) {
  return vec2(z.x * z.x - z.y * z.y, 2.0 * z.x * z.y);
}
 
float mandelbrot(vec2 c, int maxIter) {
  vec2 z = vec2(0.0);
  for (int i = 0; i < maxIter; i++) {
    z = complexSquare(z) + c;
    if (dot(z, z) > 4.0) {
      return float(i) / float(maxIter);
    }
  }
  return 0.0;
}
glsl

The dot(z, z) > 4.0 check is an optimization. Once z>2|z| > 2, the sequence will definitely escape to infinity. We can stop iterating and record how many iterations it took.

The Mandelbrot Set

Drag to pan, scroll to zoom. Increase iterations for deeper zooms.

Drag to pan around the fractal. Zoom in on the boundary and you will discover spirals within spirals, miniature copies of the whole set, and patterns that never quite repeat.

Julia Sets

The Mandelbrot set varies the constant cc while starting from z0=0z_0 = 0. Julia sets do the opposite: they fix cc and vary the starting point z0z_0.

Each point in the complex plane becomes the initial value, and we iterate:

zn+1=zn2+cz_{n+1} = z_n^2 + c

The same escape-time algorithm applies, but now each choice of cc produces a different fractal. Some are connected, intricate shapes. Others shatter into disconnected dust called Fatou dust.

Julia Set Explorer

c = -0.400 + 0.600i

Adjust c to explore different Julia sets

Drag the crosshair to change cc. Notice how the Julia set's shape relates to where cc lies relative to the Mandelbrot set. Points inside the Mandelbrot set produce connected Julia sets; points outside produce disconnected ones.

Understanding Iteration

What does it mean for a point to "escape"? Let us visualize the iteration process step by step.

Iteration Visualizer

Click to set c (yellow). Green = start, blue = iterations, red = final. Circle at radius 2 is escape threshold.

For points deep inside the set, the iteration settles into a cycle or stays near the origin. For points outside, each iteration pushes zz further away until it crosses our escape threshold. The boundary points hover on the edge, taking many iterations before their fate becomes clear.

Smooth Coloring

Counting iterations produces banding artifacts: sudden color jumps between regions that escaped at iteration 50 versus iteration 51. For smoother gradients, we use smooth iteration count:

smooth=n+1log(logz)log2\text{smooth} = n + 1 - \frac{\log(\log|z|)}{\log 2}

This formula interpolates between integer iteration counts based on how far past the escape radius the point traveled.

float smoothMandelbrot(vec2 c, int maxIter) {
  vec2 z = vec2(0.0);
  for (int i = 0; i < maxIter; i++) {
    z = complexSquare(z) + c;
    if (dot(z, z) > 256.0) {
      float smooth = float(i) - log2(log2(dot(z, z))) + 4.0;
      return smooth / float(maxIter);
    }
  }
  return 0.0;
}
glsl

We use a larger escape radius (256 instead of 4) to ensure accurate smooth coloring.

Smooth vs Banded Coloring

Toggle to compare smooth interpolation vs integer iteration counts

The smooth version produces beautiful gradients that reveal the fractal's structure without distracting color bands.

Color Mapping

The iteration count gives us a single value per pixel. Mapping this to color is where artistry enters. Common approaches:

Gradient mapping: Map iteration count through a color gradient. Sine waves with different phases create rainbow effects:

vec3 palette(float t) {
  return 0.5 + 0.5 * cos(6.28 * (t + vec3(0.0, 0.33, 0.67)));
}
glsl

Histogram equalization: Distribute colors based on how many pixels fall in each iteration range, ensuring the full palette is used.

Orbit traps: Color based on how close the orbit came to certain shapes (circles, lines, or other points), not just when it escaped.

Fractal Explorer

Drag to pan, scroll to zoom. Try different fractals and color palettes.

Experiment with different palettes and zoom levels. The fractal contains infinite variety.

Precision Limits

As you zoom deeper, floating-point precision becomes a limiting factor. Standard 32-bit floats have about 7 decimal digits of precision. After zooming by a factor of 10610^6 or so, the fractal starts looking blocky.

For deeper exploration, techniques include:

  • Double precision: Using 64-bit floats (when available) doubles the zoom depth
  • Perturbation theory: Computing deltas from a reference orbit
  • Arbitrary precision: Software floating point for unlimited depth

In WebGL, we are typically limited to 32-bit floats, so our zoom depth is constrained. Even so, there is an enormous amount of detail to explore.

Beyond Mandelbrot

The Mandelbrot set uses z2+cz^2 + c, but other formulas produce other fractals:

  • Burning Ship: Uses Re(z)|\text{Re}(z)| and Im(z)|\text{Im}(z)| in the iteration
  • Multibrot sets: Use zn+cz^n + c for different powers nn
  • Tricorn: Uses the complex conjugate: zˉ2+c\bar{z}^2 + c
  • Newton fractals: Based on Newton's method for finding roots

Each formula creates its own universe of patterns.

Key Takeaways

  • Fractals arise from simple iteration rules applied to complex numbers
  • The Mandelbrot set varies cc; Julia sets fix cc and vary the starting point
  • Escape-time coloring: count iterations until z>2|z| > 2, or mark as inside the set
  • Smooth iteration count eliminates color banding with logarithmic interpolation
  • Color palettes transform iteration counts into visual art
  • Floating-point precision limits zoom depth, but the detail is effectively infinite
  • The boundary between escape and capture contains the fractal's infinite complexity