Skip to main content
GameDev.net gamedev.net

Julia Set Explorer

by GameDev.net · GLSL ES 3.00 (WebGL2) · 25 Aug 2026

Use in your engine

Run the shader to adjust these controls.

What it demonstrates

A Julia set runs the same iteration as the Mandelbrot set with one difference: the constant is fixed for the whole image and the starting point is what varies per pixel. Swapping which of the two values comes from the screen turns one picture into an infinite family of them, and moving the constant continuously animates between shapes that look unrelated.

Source Revision 1

Author notes are linked to specific lines.

Shader inputs

void mainImage(out vec4 fragColor, in vec2 fragCoord)

Called once per pixel. Write the colour to fragColor.

iResolution vec3
Viewport size in pixels (z is the pixel aspect ratio).
iTime float
Seconds since the shader started.
iTimeDelta float
Seconds since the previous frame.
iFrameRate float
Frames per second, smoothed.
iFrame int
Frames rendered since the start.
iMouse vec4
Mouse position: xy while held, zw of the last click.
iDate vec4
Year, month, day, and seconds within the day.
iChannel0 sampler2D
Texture bound to channel 0.
iChannel1 sampler2D
Texture bound to channel 1.
iChannel2 sampler2D
Texture bound to channel 2.
iChannel3 sampler2D
Texture bound to channel 3.
iChannelResolution vec3[4]
Pixel size of each bound channel texture.
iChannelTime float[4]
Playback time of each channel, in seconds.
iSampleRate float
Audio sample rate, always 44100.
Main Image8
1 uniform vec2 uConstant; // @param -1.0..1.0 = -0.54, 0.54 "Constant c"
2 uniform bool uOrbit; // @param = 1 "Orbit the constant"
3 uniform float uZoomExponent; // @param -1.0..6.0 = 0.4 "Zoom exponent"
4 uniform int uIterations; // @param 32..300 = 160 "Iterations"
5
6 void mainImage(out vec4 fragColor, in vec2 fragCoord) {
7 vec2 screen = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
8 float span = 3.0 / exp2(uZoomExponent);
9 vec2 z = screen * span;
10 vec2 c = uConstant;
11 if (uOrbit) {
12 float angle = iTime * 0.35;
13 c = 0.7885 * vec2(cos(angle), sin(angle));
14 }
15 float count = 0.0;
16 float trap = 1000.0;
17 for (int i = 0; i < 300; i++) {
18 if (i >= uIterations) break;
19 z = vec2(z.x * z.x - z.y * z.y, 2.0 * z.x * z.y) + c;
20 trap = min(trap, abs(z.y));
21 if (dot(z, z) > 65536.0) break;
22 count += 1.0;
23 }
24 if (count >= float(uIterations)) {
25 float interior = 1.0 - exp(-6.0 * trap);
26 fragColor = vec4(mix(vec3(0.02, 0.02, 0.06), vec3(0.10, 0.06, 0.22), interior), 1.0);
27 return;
28 }
29 float smoothCount = count + 1.0 - log2(max(log2(length(z)), 0.0001));
30 float t = fract(smoothCount * 0.075 + 0.15);
31 vec3 color = vec3(0.48, 0.52, 0.58) + vec3(0.42, 0.38, 0.34) * cos(6.2831853 * (t + vec3(0.0, 0.16, 0.32)));
32 fragColor = vec4(color, 1.0);
33 }
34

Learn from this shader

How it works

The fragment coordinate becomes the initial value of the iteration and the constant comes from a control. Orbiting drives that constant around a circle just outside the Mandelbrot boundary, which is where the visually interesting Julia sets live; inside the boundary they are solid, far outside they are dust. Escaped points are coloured by the same smooth iteration count used for the Mandelbrot explorer. The interior gets its own treatment: the loop records the closest the orbit ever came to the real axis, an orbit trap, and that distance shades the inside of the set instead of leaving a flat silhouette.

Try changing

Turn Orbit off and drag Constant c yourself. The transitions are the point: small moves near the boundary reorganise the whole set, while moves further out only thin it. Reduce Iterations and watch the fine filaments disappear first, since they are the slowest points to escape. In a fork, replace the orbit trap with the distance to the origin, or trace a path through the Mandelbrot set rather than a circle.

Using it in a game

Orbit traps are the transferable idea. Recording something about a path while iterating, then shading with it, is the same technique used for glow along a raymarched surface, interior shading of translucent materials, and heat maps of loop cost. The iteration itself is a demonstration piece rather than a shippable effect.

Explore the techniques

Continue with curated explanations and progressively related examples.

LicenseMIT
Views0
Forks0

Discussion

Loading comments...