How I Built a 60 FPS Real-Time Fractal Explorer in WebGL 2.0 & Vectorized NumPy Library for 27 Complex Systems
How I Built a 60 FPS Real-Time Fractal Explorer in WebGL 2.0 & Vectorized NumPy Library for 27 Complex Systems πβ‘ Fractals are infinite mathematical universes generated by deceptively simple rules. Most of us have seen
How I Built a 60 FPS Real-Time Fractal Explorer in WebGL 2.0 & Vectorized NumPy Library for 27 Complex Systems πβ‘
Fractals are infinite mathematical universes generated by deceptively simple rules. Most of us have seen the classical Mandelbrot set ($z_{n+1} = z_n^2 + c$), but the mathematical universe of dynamical systems is vastly largerβspanning non-analytic systems, rational physics maps, and complex analytic continuations.
Over the past week, I set out to build FantasticFractals: a dual-engine project consisting of:
- A 60 FPS interactive web visualizer powered by custom WebGL 2.0 GPU fragment shaders running in under 3 milliseconds per frame.
-
A standalone Python scientific library on PyPI (
fantasticfractals) with vectorized NumPy compute kernels, Hausdorff box-counting dimension analysis, and 16K Ultra-HD export.
Here is the engineering journey of how it was built, why standard canvas loops melt the CPU, and how GLSL shaders and NumPy array broadcasting make infinite mathematical art run seamlessly.
π₯ The Problem: Why Traditional Canvas Loops Fail
At 1080p resolution ($1920 \times 1080$), a single frame contains 2,073,600 pixels.
If you calculate an escape-time fractal with an iteration ceiling of 250 steps, computing one frame on a standard CPU thread requires up to 518,400,000 operations.
CPU Canvas 2D Loop:
For every Y (1080):
For every X (1920):
While |z| < 2 and n < 250:
z = z^2 + c
-> Takes 8,000ms - 12,000ms (10+ seconds of UI freezing!)
When I tested this with serverless edge functions and CPU workers, dragging the canvas or zooming triggered severe lag (10+ seconds per render). It was completely unusable for interactive exploration.
β‘ The Solution: Hardware GPU Acceleration via WebGL 2.0 (2ms Compute)
To achieve silky-smooth 60 frames per second, we offloaded the entire mathematical calculation to the GPU using WebGL 2.0 fragment shaders.
In WebGL, your graphics card executes thousands of pixel computations concurrently across its stream processors. Instead of computing pixels one by one in a serial loop, every pixel on your screen computes its own escape trajectory simultaneously in under 3 milliseconds.
The Core GLSL Escape Loop
Here is how the complex arithmetic executes directly inside our fragment shader:
#version 300 es
precision highp float;
in vec2 vUv;
out vec4 fragColor;
uniform vec2 u_center;
uniform float u_zoom;
uniform int u_max_iterations;
uniform sampler2D u_palette_lut;
void main() {
// Map normalized screen coordinates to complex plane
vec2 c = u_center + (vUv - 0.5) * (3.0 / u_zoom);
vec2 z = c;
int iter = 0;
float smoothVal = 0.0;
for (int i = 0; i < 500; i++) {
if (i >= u_max_iterations) break;
// Complex multiplication: (x + iy)^2 = (x^2 - y^2) + i(2xy)
float x2 = z.x * z.x;
float y2 = z.y * z.y;
if (x2 + y2 > 4.0) {
// Smooth continuous exterior potential:
// nu = n + 1 - ln(ln|z|) / ln(2)
float log_zn = log(x2 + y2) / 2.0;
float nu = log(log_zn / log(2.0)) / log(2.0);
smoothVal = float(i) + 1.0 - nu;
iter = i;
break;
}
z = vec2(x2 - y2, 2.0 * z.x * z.y) + c;
}
// Sample color from precomputed 256-color LUT texture
float u = fract(smoothVal / 40.0);
fragColor = texture(u_palette_lut, vec2(u, 0.5));
}
By uploading a 1D palette Lookup Table (LUT) as a GPU texture, color interpolation and continuous shading happen at native hardware speed.
π Beyond Mandelbrot: 27 Mathematical Fractals
Most visualizers stop at Mandelbrot and Julia. FantasticFractals provides complete analytical support for 27 distinct mathematical fractal systems organized into 7 mathematical families:
| Category | Famous Examples | Mathematical Formulation |
|---|---|---|
| Classical Escape | Mandelbrot, Julia | $z_{n+1} = z_n^2 + c$ |
| Non-Analytic | Burning Ship, Buffalo, Celtic | $z_{n+1} = ( |
| Conjugate Escape | Tricorn (Mandelbar), Multibar | $z_{n+1} = \bar{z}_n^2 + c$ |
| Higher Order | Multibrot 3, Multibrot 4 | $z_{n+1} = z_n^d + c$ ($d=3, 4$) |
| Convergence Basins | Newton-Raphson, Nova | $z_{n+1} = z_n - R \frac{z_n^3 - 1}{3z_n^2} + c$ |
| Physics & Rational | Magnet 1 & 2 (Potts model), Feather | $z_{n+1} = \left(\frac{z^2 + c - 1}{2z + c - 2}\right)^2$ |
| Geometric & Dynamical | Sierpinski, Koch, Collatz, Lyapunov | Analytic continuation of $3n+1$ into $\mathbb{C}$ |
π¬ The Evolution Slideshow Engine
Fractals are iterative limits ($n \to \infty$). But what does a fractal look like at iteration 1? At iteration 5? At iteration 35?
We built a uniform Evolution Slideshow Engine that lets you scrub through time:
- As the iteration ceiling steps forward ($1 \to 2 \to 3 \to \dots \to 250$), you watch geometric boundaries fold, branch, and crystallize into chaotic attractors.
- Includes an interactive scrubber, variable speed playback (5 to 60 iterations/sec), and Ping-Pong oscillation mode.
π Building the Python Scientific Library (pip install fantasticfractals)
To bring these mathematical systems to data science notebooks, research pipelines, and high-resolution rendering, we packaged a standalone Python library and published it to PyPI:
pip install fantasticfractals
1. Vectorized NumPy Broadcasting
Instead of slow nested Python loops, algorithms are implemented using NumPy array broadcasting:
from fantasticfractals import render_fractal, export_image
# Render a 4K Burning Ship wallpaper
img = render_fractal(
fractal_type="burning_ship",
width=3840,
height=2160,
palette="cyberpunk",
max_iterations=300
)
img.save("burning_ship_4k.png")
2. Empirical Hausdorff / Minkowski-Bouligand Box Dimension
The library includes automated box-counting dimension analysis $D = \lim_{\epsilon \to 0} \frac{\log N(\epsilon)}{\log(1/\epsilon)}$:
from fantasticfractals import FractalEngine, compute_box_dimension
engine = FractalEngine()
grid = engine.render_raw("sierpinski_triangle", width=1024, height=1024, max_iter=25)
dim, r_squared, scales, counts = compute_box_dimension(grid)
print(f"Calculated Box Dimension: {dim:.4f} (RΒ² = {r_squared:.4f})")
# Expected for Sierpinski Triangle: log(3)/log(2) β 1.5850
π Try It Out (Links & Open Source)
Everything is open source under the MIT License:
- π Live Interactive Web App: https://fantasticfractals.pages.dev
- π¦ PyPI Package: https://pypi.org/project/fantasticfractals/
- π GitHub Repository: https://github.com/QuantumTechCoder/FantasticFractals
- π Interactive 1-Click Google Colab: Open in Colab
If you enjoy mathematical art and creative coding, feel free to give the repository a star or try zooming into the Burning Ship antenna! What fractal system should we implement next? Let me know in the comments below!
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.