Jan 30, 2026 · 6 min read

Exploring the Mandelbrot Set in OCaml

Image for Exploring the Mandelbrot Set in OCaml

There’s something deeply satisfying about a piece of math that produces infinite complexity from almost nothing. The Mandelbrot set is exactly that — a simple equation, iterated over and over, that draws the boundary between order and chaos across the complex plane.

I built an interactive viewer for it in OCaml using SDL2. The entire thing fits in a single file. This post walks through the math that makes it work.

Complex Numbers in 30 Seconds

A complex number has two parts: a real part and an imaginary part. We write it as a+bia + bi, where i=1i = \sqrt{-1}. You can think of complex numbers as points on a 2D plane — the real part is the x-axis, the imaginary part is the y-axis. This is called the complex plane.

The key operation is multiplication. When you multiply two complex numbers:

(a+bi)(c+di)=(acbd)+(ad+bc)i(a + bi)(c + di) = (ac - bd) + (ad + bc)i

Geometrically, multiplication stretches and rotates. Squaring a complex number doubles its angle from the origin and squares its distance. This stretching-and- rotating behavior is exactly what makes the Mandelbrot set interesting.

The Definition

The Mandelbrot set is defined by a single recurrence:

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

Start with z0=0z_0 = 0. Pick any point cc on the complex plane. Now iterate: square zz, add cc, repeat. One of two things happens:

  1. The sequence escapesz|z| grows without bound, shooting off toward infinity.
  2. The sequence stays boundedz|z| never exceeds some finite value, no matter how many times you iterate.

The Mandelbrot set is the set of all points cc for which the sequence stays bounded. That’s the entire definition.

The Escape Condition

In practice, you can’t iterate forever. But there’s a useful shortcut: if z|z| ever exceeds 2, the sequence is guaranteed to escape. The magnitude z=a2+b2|z| = \sqrt{a^2 + b^2}, and to avoid the square root we check:

a2+b2>4a^2 + b^2 > 4

This is why the code uses 4.0 as the threshold:

let iterations x0 y0 =
  let c = { Complex.re = x0; im = y0 } in
  let z = ref Complex.zero in
  let iter = ref 0 in
  while !iter < max_iteration
        && (!z).Complex.re *. (!z).Complex.re
           +. (!z).Complex.im *. (!z).Complex.im <= 4.0 do
    z := Complex.add (Complex.mul !z !z) c;
    incr iter
  done;
  !iter

For each pixel on screen, we map it to a point cc on the complex plane and count how many iterations it takes for z2|z|^2 to exceed 4. If it never does within max_iteration steps, we assume the point is in the set.

Why the Boundary Is Fractal

The boundary of the Mandelbrot set — the edge between points that escape and points that don’t — is where things get strange. Points near the boundary take many iterations to escape. Points just barely inside the set take many iterations before settling into a bounded orbit. This sensitivity to initial conditions means that no matter how far you zoom in, you find new structure. The boundary has infinite detail at every scale.

This is what makes it a fractal. The boundary’s Hausdorff dimension is 2, meaning it’s so convoluted that it’s effectively as complex as a filled area despite being a one-dimensional curve.

Coloring

Points inside the set are colored black — they never escape. For points outside the set, the number of iterations before escape gives a natural way to assign color. Low iteration counts (quick escapes) get one color; high counts (slow escapes) get another. The coloring function maps the iteration count to an RGB value packed into ARGB8888 format for SDL2:

let color_of_iter n =
  if n = max_iteration then
    0xFF000000l  (* black — in the set *)
  else
    let v = int_of_float (255.0 *. float n /. float max_iteration) in
    Int32.of_int (0xFF000000 lor (v lsl 16) lor ((v / 2) lsl 8) lor (255 - v))

This produces a gradient from blue (quick escape) through green to red (slow escape), with the set itself in black. More sophisticated coloring schemes exist — smooth iteration counts, histogram equalization, orbit traps — but this simple mapping already reveals the structure.

Rendering with SDL2

The viewer uses SDL2 via OCaml’s tsdl bindings. The approach is straightforward: create a streaming texture with ARGB8888 pixel format, lock it to get a Bigarray. int32 buffer, write every pixel, unlock, and present. One frame, one pass, no per- pixel draw calls.

let redraw renderer texture xmin xmax ymin ymax =
  let (pixels, pitch) =
    Sdl.lock_texture texture None Bigarray.int32 |> or_exit
  in
  draw_mandelbrot pixels pitch xmin xmax ymin ymax;
  Sdl.unlock_texture texture;
  Sdl.render_clear renderer |> ignore;
  Sdl.render_copy renderer texture |> ignore;
  Sdl.render_present renderer

SDL’s coordinate system has y=0y = 0 at the top of the window, which is the opposite of the mathematical convention. The code compensates by mapping SDL row 0 to ymaxy_{max} and row h1h - 1 to yminy_{min}.

Zooming and the Infinite Detail

The viewer supports interactive zoom. Click a point and the viewport shrinks by half around it, doubling the magnification. This is where the fractal nature becomes tangible — you can zoom in dozens of times and still find new spirals, miniature copies of the full set, and filaments connecting them.

Mathematically, zooming just changes the window [xmin,xmax]×[ymin,ymax][x_{min}, x_{max}] \times [y_{min}, y_{max}] on the complex plane. The computation is identical at every scale; only the coordinates change. The set itself has no preferred scale.

At extreme zoom levels, floating-point precision becomes the limiting factor. Standard 64-bit float gives roughly 15 significant digits, which allows roughly 25210152^{52} \approx 10^{15} times magnification before the pixels stop resolving new detail. Going deeper requires arbitrary-precision arithmetic or perturbation theory — topics for another day.

Building and Running

The project requires OCaml, opam, and SDL2:

# install SDL2
sudo apt-get install libsdl2-dev  # Debian/Ubuntu
brew install sdl2                  # macOS

# build and run
opam install . --deps-only
dune exec mandelbrot

Click to zoom in. Press +/- to zoom at the cursor, hjkl or wasd to pan, r to reset, q to quit. The source is on GitHub.

Further Reading

The Mandelbrot set sits at the intersection of complex dynamics, number theory, and computer graphics. A few starting points for going deeper:

  • Benoit Mandelbrot’s The Fractal Geometry of Nature — the book that started it all
  • The Ewing and Schober proof that the Mandelbrot set is connected
  • Mu-Ency — an encyclopedia of the Mandelbrot set and its features
  • Perturbation theory for deep zooming: the technique behind modern ultra-deep renderers