Scientific ComputingIntermediate

Mandelbrot Set Fractal in Python

Render the Mandelbrot set in Python with NumPy and Matplotlib. Vectorized fractal generation, deep zooms, and Julia sets — runnable in your browser.

Try it yourself

Run this code directly in your browser. Click "Open in full editor" to experiment further.

Loading...

Click Run to see output

Or press Ctrl + Enter

How it works

The Mandelbrot set is one of the most famous objects in mathematics. It's also one of the most surprising — an entire universe of infinite, self-similar detail emerges from a formula so simple you can write it on a sticky note: z = z² + c.

What The Mandelbrot Set Actually Is

Pick any complex number c. Then run this loop:

z = 0
repeat:
    z = z*z + c

One of two things will happen:

  • `z` blows up to infinityc is not in the Mandelbrot set.
  • `z` stays bounded foreverc is in the Mandelbrot set.
  • If you check every point in the complex plane and color the "in" points black, you get the iconic Mandelbrot silhouette: a heart-shaped main body with a circle bulb stuck to the left, surrounded by progressively smaller copies of itself.

    The colorful halo around it isn't part of the set — it's a visualization of how fast each outside point escaped. Points that escape after 2 iterations get one color, points that take 200 iterations get another. That escape speed is what creates the fractal's shimmering, infinitely detailed boundary.

    The One Mathematical Shortcut You Need

    There's a beautiful theorem hiding here: if `|z|` ever exceeds 2, the sequence is guaranteed to escape to infinity. So you don't have to wait forever to know a point is outside the set — the moment |z| > 2, you can stop iterating and record the count. This is what max_iter is for: a budget. If a point hasn't escaped within that budget, we treat it as "in" the set.

    Why You Vectorize With NumPy

    The naive way to render this is a triple loop: for each pixel row, for each column, run the iteration loop. For a 600×600 image with 200 iterations, that's 72 million Python-level operations — agonizingly slow.

    The NumPy version in the snippet does the same math, but on the entire grid at once. Z = Z**2 + C updates 360,000 complex numbers in a single CPU-friendly operation. The result is hundreds of times faster.

    A few details that make the vectorized version work:

  • x[np.newaxis, :] + 1j * y[:, np.newaxis] builds the whole 2D grid of complex numbers in one expression using broadcasting.
  • Z[alive] = Z[alive] ** 2 + C[alive] only updates the pixels that haven't escaped yet — once a pixel escapes, there's no point doing more math on it.
  • alive &= ~escaped_now keeps a running boolean mask of which pixels are still being iterated.
  • The early-exit if not alive.any(): break catches the case where the entire image has escaped (common when zoomed far outside the set).
  • Choosing `max_iter` — The Trade-off

    Low max_iter is fast but loses detail near the boundary — points that would escape eventually get incorrectly classified as "in the set". High max_iter reveals more of the fractal's intricate edge but costs proportionally more computation.

    A practical rule:

  • Whole-set view → 100-200 iterations is plenty.
  • Mild zoom → bump to 300-500.
  • Deep zoom → 1000-5000+. The deeper you go, the more iterations you need just to render the boundary cleanly.
  • Where To Zoom

    The boundary of the set is where the magic lives. A few famous neighborhoods worth visiting:

  • Seahorse Valley — the indentation around (-0.75, 0.1). Spirals everywhere.
  • Elephant Valley — around (0.275, 0). Look for trunks.
  • Mini Mandelbrots — copies of the whole set hidden along the boundary at any depth, e.g. around (-1.75, 0).
  • Tendrils near `(-0.75, 0)` — long lightning-like filaments.
  • The deeper you zoom, the more bizarre the structures get. The set is provably self-similar at every scale — there is no "bottom" to reach.

    Color Palettes Matter

    The escape-iteration map is just integers. The visual character comes entirely from the colormap:

  • 'twilight_shifted' — soft, dreamlike
  • 'inferno' / 'magma' — fiery, dramatic
  • 'twilight' — purple/cyan, very modern
  • 'hot' — high-contrast classic
  • 'gnuplot2' — psychedelic
  • For a really polished look, use smooth coloring: iter + 1 - log(log(|z|)) / log(2) instead of integer iteration counts. It eliminates the visible "banding" in the colors.

    Julia Sets — The Same Idea, Inside Out

    The Mandelbrot set lives in parameter space — each pixel is a different value of c. Julia sets live in dynamical spacec is fixed, and each pixel is a different starting z.

    Every c in the complex plane has its own Julia set. Pick c from inside the Mandelbrot set and the Julia set is a connected blob. Pick c from outside and it shatters into a Cantor-like dust. The boundary points of the Mandelbrot set produce the most beautiful Julia sets — fractal swirls, lightning, and dragons.

    A few c values that produce iconic Julia sets:

  • c = -0.7 + 0.27015i — a lacy, dragon-like shape
  • c = -0.8 + 0.156i — flowing tendrils
  • c = 0.285 + 0.01i — crystalline spirals
  • c = -0.4 + 0.6i — "the seahorse"
  • Things To Try Next

  • Smooth coloring — replace integer iteration counts with a continuous escape-time measure for buttery-smooth color gradients.
  • Animate a zoom — generate frames at progressively smaller windows and stitch them into a video. Some YouTube zoom videos go to 10⁻¹⁰⁰ scales.
  • 3D fractals — the Mandelbulb and Mandelbox extend the same idea to 3D. Much more compute-intensive.
  • Buddhabrot — instead of coloring the destination of each escape, plot every point each escaping orbit visits. Produces a ghostly, completely different image of the same set.
  • Run the snippet above and you'll see the full set in moments, dive into a region of detail that didn't even exist at the previous zoom level, and watch four Julia sets emerge from the same algorithm just by changing one constant.

    Related examples