Skip to content
Sample of Lorenz Attractor (3D ODE) — strange-attractor trajectory with depth shading

shader_legends_2 · LorenzAttractor3D

Lorenz Attractor (3D ODE) — strange-attractor trajectory with depth shading

The Lorenz system (1963) is the canonical strange-attractor: x' = sigma(y - x); y' = x(rho - z) - y; z' = xy - beta*z. With the classical parameters (sigma=10, rho=28, beta=8/3) the trajectory spirals around two unstable foci and oscillates between them indefinitely without ever repeating — a deterministic, bounded, non-periodic curve shaped like a butterfly. This formula integrates the ODE with RK4 and renders the resulting 3D curve as a fading trail on a 2D canvas, using z for depth-shaded brightness and a per-step cosine-palette hue along the trajectory length.

Original: https://en.wikipedia.org/wiki/Lorenz_system

▶ Try the live demo (JS port) ▶ Run reference.py via Pyodide 10 formulas have instant JS ports. The Pyodide path runs the actual Python in your browser — first load is ~6 MB, then instant.

Family
shader_legends_2
Source
shader legends II
Evolution
attractor
Render
path_tracing
Complexity
medium
Agents
0
Year
1963
Author
Edward N. Lorenz (1963)

Math essence

Initial condition (x0, y0, z0) ~ (1, 1, 25). RK4 integration of the three ODEs over trajectory_length steps with fixed dt. Orthographic project (rotate by 'rotation' about y, drop z'). z_norm = (z - z_bias*22)/24 normalised into [0, 1]; per-step hue = index/N mapped through a + b*cos(2pi*(c*(h + palette_offset) + d)); pixels splatted with line_glow halo; depth = sigmoid(2.5*z_norm).

Parameters

Name Type Default Range Description
dim int 360 120–720 Square output size in pixels.
sigma float 10 2–30 Prandtl number; standard value is 10.
rho float 28 5–80 Rayleigh number; standard value 28 — values < ~13.9 give a stable focus, between produce pre-chaotic orbits, above are chaotic.
beta float 2.6666666 0.5–8 Geometric factor; standard value 8/3 ~ 2.667.
trajectory_length int 4096 256–16384 Number of RK4 integration steps before rendering.
dt float 0.012 0.001–0.05 RK4 time step.
line_glow float 1.6 0.3–4 Strength of the per-pixel neighbour halo.
z_bias float 0.5 -1–2 Bias of the z normalisation midpoint (higher shifts the focus nearer/farther).
rotation float 0.6 -3.14–3.14 Rotation of (x, z) about y before projection.
palette_offset float 0.5 0–1 Phase offset for the per-step cosine palette.
time_speed float 0 -0.2–0.2 Drift speed of the initial-condition orbit per step (0 = static frame).

Reference

First 60 lines of reference.py. Full source: library_external/shader_legends_2/LorenzAttractor3D/reference.py

from __future__ import annotations

from typing import ClassVar

import numpy as np


def _rk4_lorenz(
    x0: float, y0: float, z0: float,
    sigma: float, rho: float, beta: float,
    dt: float, steps: int,
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
    """RK4 integration of the Lorenz system

        x' = sigma (y - x)
        y' = x (rho - z) - y
        z' = x y - beta z

    Returns arrays (X, Y, Z) of the trajectory.  Steps use fixed
    dt and no early termination; callers can downsample the
    trajectory for rendering.
    """
    X = np.empty(steps, dtype=np.float32)
    Y = np.empty(steps, dtype=np.float32)
    Z = np.empty(steps, dtype=np.float32)
    X[0] = x0
    Y[0] = y0
    Z[0] = z0
    x, y, z = x0, y0, z0

    def f(x: float, y: float, z: float) -> tuple[float, float, float]:
        return (
            sigma * (y - x),
            x * (rho - z) - y,
            x * y - beta * z,
        )

    for k in range(1, steps):
        k1x, k1y, k1z = f(x, y, z)
        k2x, k2y, k2z = f(x + 0.5 * dt * k1x, y + 0.5 * dt * k1y, z + 0.5 * dt * k1z)
        k3x, k3y, k3z = f(x + 0.5 * dt * k2x, y + 0.5 * dt * k2y, z + 0.5 * dt * k2z)
        k4x, k4y, k4z = f(x + dt * k3x, y + dt * k3y, z + dt * k3z)
        x = x + (dt / 6.0) * (k1x + 2.0 * k2x + 2.0 * k3x + k4x)
        y = y + (dt / 6.0) * (k1y + 2.0 * k2y + 2.0 * k3y + k4y)
        z = z + (dt / 6.0) * (k1z + 2.0 * k2z + 2.0 * k3z + k4z)
        X[k] = x
        Y[k] = y
        Z[k] = z
    return X, Y, Z


class LorenzAttractor3D:
    """Lorenz attractor, 2D-projected with depth shading.

    The Lorenz system (1963, E. N. Lorenz) is a 3D ODE that emerges
    in atmospheric convection.  It is the canonical "strange
    attractor" — a deterministic, bounded, non-periodic trajectory
    shaped like a butterfly that chaos theory has used as an icon
    for six decades.

Try running it locally

The reference implementation is self-contained Python with numpy. Install the package and run:

python library_external/shader_legends_2/LorenzAttractor3D/reference.py

Need help? See Learn for the standard environment setup, or Playground for an in-browser sketchbook.

More from shader legends II

Sample of Hex Grid Tiling — axial-coord tessellation with fBM-filled cells

Hex Grid Tiling — axial-coord tessellation with fBM-filled cells

shader_legends_2 · HexGridTiling

A hexagonal grid is the canonical 6-fold-symmetric alternative to the square-grid hash tiling.

View →
Sample of IQ Domain Warping — fbm composed with itself at 2/3 levels

IQ Domain Warping — fbm composed with itself at 2/3 levels

shader_legends_2 · IQDomainWarping

Inigo Quilez's domain warping trick: feed a noise function *itself* as input coordinates, producing organic flowing patterns that no plain noise function can match.

View →
Sample of IQ Mandelbox (2D edition) — iterated box-fold / ball-fold / scale / translate

IQ Mandelbox (2D edition) — iterated box-fold / ball-fold / scale / translate

shader_legends_2 · IQMandelbox2D

The Mandelbox is a 3D fractal defined by the iterate v := scale * box_fold(ball_fold(v)) + c, parametrised by a 'scale' fold factor and the (r_min, r_fixed) ball-fold thresholds.

View →
Sample of IQ Voronoi (cellular noise) — F1 / F2 distance field

IQ Voronoi (cellular noise) — F1 / F2 distance field

shader_legends_2 · IQVoronoi

Inigo Quilez's classical 2D Voronoi / cellular noise: for each integer cell of the plane a single feature point is placed at cell_centre + jitter*(hash-0.5).

View →
Sample of Menger Sponge, 2D Carpet edition — recursive 3x3 / centre-removal

Menger Sponge, 2D Carpet edition — recursive 3x3 / centre-removal

shader_legends_2 · MengerSponge2D

The Menger sponge (1926) is a 3D fractal formed by sub-dividing each cube into 3x3x3 and removing the centre plus the six face-centres.

View →

See all shader_legends_2 artworks →