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.
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.
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.
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.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.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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Edit and run 20 canvas, WebGL, three.js and CSS animations live in your browser.
Draw spirograph and harmonograph figures in your browser. Adjust the frequency ratio, radii and damping, then save a PNG.
Generate the Matrix digital rain effect in your browser. Tune speed, glyphs, colour and trail length, then export a PNG, video or standalone HTML file.