Mandelbrot Explorer

Zoom into the Mandelbrot set in real time. GPU-rendered in your browser with adjustable iterations, colour cycling, Julia sets, PNG export and video recording. No install, nothing uploaded.

Pick an animation

All 28 animations →

Canvas 2D
three.js
WebGL
CSS / DOM
Mandelbrot Explorer

Live controls

0.32
180
3
0
0.2

Knobs patch the running animation live. A few (marked in the code as baked-in values like particle counts) restart the preview.

Escape-time fractal you can actually dive into. Scroll to zoom.

Your code runs in a sandboxed frame with no access to this page, and it is never sent to a server. three.js demos load the library from jsDelivr; the rest need nothing but the browser.

Advertisement

Explore the Mandelbrot set in real time

The Mandelbrot set is the set of complex numbers c for which the sequence z → z² + c, starting from zero, never runs away to infinity. Everything you see here comes from repeating that one line per pixel and asking how many repeats it took to escape.

This explorer renders it on the GPU, so panning and zooming stay smooth instead of redrawing a bitmap on the CPU. Drag to move, scroll to zoom, and use the controls to tune the render.

Controls

  • Iterations — how many times to repeat z² + c before deciding a point never escapes. The set's boundary is infinitely detailed, so the deeper you zoom the more iterations you need before the edge stops looking like mush. Raise it as you descend; lower it if the frame rate drops.
  • Colour cycles — how many times the palette repeats across the escape range. High values give tight contour bands, low values give broad gradients.
  • Hue — rotates the palette.
  • Julia set — swaps the rule. Instead of using each pixel as c and starting z at zero, every pixel becomes the starting z and c is a single travelling seed. The seed orbits a circle, so the shape continuously morphs. Julia sets and the Mandelbrot set are two views of the same equation: each point in the Mandelbrot set corresponds to a connected Julia set.
  • Seed speed — how fast that Julia seed travels.

Why the colours are smooth

A naive renderer colours each pixel by its integer escape count, which produces visible stair-stepped bands. This one uses the continuous escape count:

smooth = n + 1 - log(log(|z|)) / log(2)

That fractional value varies continuously across the boundary, so the bands blend instead of stepping. It is the single change that separates a fractal renderer that looks amateur from one that does not.

Where to zoom

  • The seahorse valley, the pinched channel between the main cardioid and the large left-hand bulb, is the classic first dive.
  • Mini-brots — tiny complete copies of the whole set, scattered along the boundary at every scale. Finding one is proof of the set's self-similarity.
  • The antenna, the spike running out to the left, is dense with detail at high iteration counts.

Precision is limited by 32-bit floats on the GPU, so at extreme zoom the image will eventually go blocky. That is the hardware, not the maths.

Saving your work

Save PNG captures the current frame at the canvas's full resolution. Record captures a WebM video, which is the better option for a Julia morph or a slow zoom. Both run entirely in your browser — no image is ever uploaded to a server.

How the renderer works

Everything you see is one fragment shader. The GPU runs it once per pixel, with thousands of pixels evaluated in parallel, and nothing is stored between frames — the colour of a pixel is a pure function of its coordinates, the zoom, and the iteration budget. That is why panning stays interactive instead of taking seconds per frame the way a CPU renderer does.

Complex multiplication, written out

GLSL has no complex number type, so z^2 + c is expanded by hand into a two-component vector:

z = vec2(z.x * z.x - z.y * z.y, 2.0 * z.x * z.y) + c;

The real part is x^2 - y^2 and the imaginary part is 2xy. That single line, repeated, is the entire Mandelbrot set.

Why the loop looks strange

for (int k = 0; k < 600; k++) {
  if (float(k) >= uIter) break;
  ...
}

WebGL 1 requires loop bounds to be compile-time constants — you cannot write k < uIter where uIter is a uniform, because the shader compiler needs to be able to unroll the loop. The workaround is to loop to a fixed maximum and break early on the real limit. The constant 600 is therefore a hard ceiling on the iteration slider, not an arbitrary number.

Escaping without a square root

The escape test is written as dot(z, z) > 256.0 rather than length(z) > 16.0. Both say the same thing, but length computes a square root and this runs on every iteration of every pixel. Comparing squared magnitudes avoids millions of unnecessary square roots per frame.

The generous escape radius of 16 (rather than the mathematically sufficient 2) also improves the smooth-colouring formula: the continuous escape estimate is more accurate the further past the boundary the point has travelled when you sample it.

Precision, performance, and where it breaks down

The zoom wall

GPUs work in 32-bit floating point, which carries roughly 7 significant decimal digits. Every pixel is computed from screen position / zoom, so as the zoom climbs, adjacent pixels start resolving to the same representable number. Around 10^6 to 10^7 magnification the image goes blocky and stops gaining detail no matter how many iterations you allow.

Renderers that go deeper use double precision (available on some hardware at a large speed penalty) or arbitrary-precision arithmetic combined with perturbation theory, where one high-precision reference orbit is computed on the CPU and nearby pixels are calculated as small offsets from it. That is how the famous deep-zoom videos are made, and it is far too slow to be interactive.

What actually costs you frames

Cost scales with pixels multiplied by iterations. Doubling the iteration slider roughly halves the frame rate; so does moving to a display with twice the pixel ratio. Points inside the set are the expensive ones, because they never escape and always run the full loop — which is why zooming into a large black region feels heavier than skimming the boundary.

If the preview stutters, drop the iterations before anything else. You will lose fine boundary detail and gain frame rate immediately.

A note on the black region

Points that never escape are drawn black because the escape count carries no information for them. There are other conventions — colouring by how close the orbit came to the origin, or by the period of the cycle it settled into — which reveal structure inside the set rather than a flat silhouette.

Frequently Asked Questions

How deep can I zoom?+

Until 32-bit floating point precision runs out on the GPU, which in practice is somewhere around 10^6 to 10^7 magnification depending on your hardware. Past that the image goes blocky - neighbouring pixels compute identical values because the numbers can no longer represent the difference between them. That is a hardware limit, not a bug. Renderers that go deeper switch to double precision or arbitrary-precision arithmetic, which is far slower.

Why does the image turn to mush when I zoom in?+

You need more iterations. The boundary of the set is infinitely detailed, and the iteration count is your budget for resolving it - too few and the renderer gives up before it can tell an escaping point from a trapped one, so everything blurs into a single band. Raise the Iterations slider as you descend. The trade-off is frame rate, since every pixel runs the loop.

What is the difference between the Mandelbrot set and a Julia set?+

They are two views of the same equation. For the Mandelbrot set, each pixel supplies the constant c and the sequence always starts at zero. For a Julia set, c is one fixed value for the whole image and each pixel supplies the starting z. Every point in the Mandelbrot set corresponds to a Julia set that is connected; every point outside corresponds to one that is scattered dust. Turning on the Julia toggle animates c around a circle so you can watch the family morph.

Can I save the image?+

Yes. Save PNG captures the current frame at the canvas resolution, and Record captures a WebM video, which is the better choice for a zoom or a Julia morph. Both happen entirely in your browser - nothing is uploaded.

Is this doing the maths on my graphics card?+

Yes. The whole image is a fragment shader, so the escape-time loop runs once per pixel with thousands of pixels computed in parallel on the GPU. That is why panning and zooming stay interactive instead of taking seconds per frame the way a CPU renderer would.

Related tools

This tool is provided for informational and educational purposes only. All processing happens in your browser — no data is sent to or stored on our servers. While we strive for accuracy, we make no warranties about the completeness or reliability of results.