Groks Idea of a Luminous 3D Fractal

Abandon the urge to simplify everything, to look for formulas and easy answers, and to begin to think multidimensionally, to glory in the mystery and paradoxes of life.

~ Scott Peck

First, i hope everyone is safe. Second, I’m writing a SnakeByte that is very near and dear to my heart, fractals. Which is actually encapsulated in the areas of complexity theory, Fibonacci sequences, and the Golden Ratio, ϕ. The world around Us and Our Universe is teeming with these self-similar geometries. Also, sometime ago, i wrote a blog on the mathematics of Grief and how I thought it was fractal-based, never-ending, ever-evolving, entitled “It’s An Honor To Say Goodbye”.

For example:

  • Golden Ratio: Appears in fractals as a scaling factor for self-similar structures (e.g., golden spirals, golden triangles) and in the proportions of natural fractals.
  • Fibonacci Sequence: Manifests in the counting of fractal elements (e.g., spirals in sunflowers, branches in trees) and converges to ϕ, linking to fractal scaling.
  • Mandelbrot Set: Contains spiral patterns that can approximate ϕ-based logarithmic spirals, especially near the boundary.
  • Nature and Art: Both fractals and Fibonacci patterns appear in natural growth and aesthetic designs, reflecting universal mathematical principles as well as our own bodies.

Fractals are mesmerizing mathematical objects that exhibit self-similarity, meaning their patterns repeat at different scales. Their intricate beauty and complexity make them a fascinating subject for mathematicians, artists, and programmers alike. In this blog, i’l dive into the main types of fractals and then create an interactive visualization of the iconic Mandelbrot set using Python and Bokeh, complete with adjustable parameters. get ready Oh Dear Readers i went long in the SnakeTooth.

Like What Are Fractals?

A fractal is a geometric shape that can be split into parts, each resembling a smaller copy of the whole. Unlike regular shapes like circles or squares, fractals have a fractional dimension and display complexity at every magnification level. They’re often generated through iterative processes or recursive algorithms, making them perfect for computational exploration.

Fractals have practical applications too, from modeling natural phenomena (like tree branching or mountain ranges) to optimizing antenna designs and generating realistic graphics in movies and games.

Types of Fractals

Fractals come in various forms, each with unique properties and generation methods. Here are the main types:

Geometric Fractals

Geometric fractals are created through iterative geometric rules, where a simple shape is repeatedly transformed. The result is a self-similar structure that looks the same at different scales.

  • Example: Sierpinski Triangle
    • Start with a triangle, divide it into four smaller triangles, and remove the central one. Repeat this process for each remaining triangle.
    • The result is a triangle with an infinite number of holes, resembling a lace-like pattern.
    • Properties: Perfect self-similarity, simple recursive construction.
  • Example: Koch Snowflake
    • Begin with a straight line, divide it into three parts, and replace the middle part with two sides of an equilateral triangle. Repeat for each segment.
    • The curve becomes infinitely long while enclosing a finite area.
    • Properties: Continuous but non-differentiable, infinite perimeter.

Algebraic Fractals

Algebraic fractals arise from iterating complex mathematical functions, often in the complex plane. They’re defined by equations and produce intricate, non-repeating patterns.

  • Example: Mandelbrot Set (the one you probably have seen)
    • Defined by iterating the function z_n+1=z_n^2+c, where z and c are complex numbers.
    • Points that remain bounded under iteration form the Mandelbrot set, creating a black region with a colorful, infinitely complex boundary.
    • Properties: Self-similarity at different scales, chaotic boundary behavior.
  • Example: Julia Sets
    • Similar to the Mandelbrot set but defined for a fixed c value, with z varying across the plane.
    • Each c produces a unique fractal, ranging from connected “fat” sets to disconnected “dust” patterns.
    • Properties: Diverse shapes, sensitive to parameter changes.

Random Fractals

Random fractals incorporate randomness into their construction, mimicking natural phenomena like landscapes or clouds. They’re less predictable but still exhibit self-similarity.

  • Example: Brownian Motion
    • Models the random movement of particles, creating jagged, fractal-like paths.
    • Used in physics and financial modeling.
    • Properties: Statistically self-similar, irregular patterns. Also akin to the tail of a reverb. Same type of fractal nature.
  • Example: Fractal Landscapes
    • Generated using algorithms like the diamond-square algorithm, producing realistic terrain or cloud textures.
    • Common in computer graphics for games and simulations.
    • Properties: Approximate self-similarity, naturalistic appearance.

Strange Attractors

Strange attractors arise from chaotic dynamical systems, where iterative processes produce fractal patterns in phase space. They’re less about geometry and more about the behavior of systems over time.

  • Example: Lorenz Attractor
    • Derived from equations modeling atmospheric convection, producing a butterfly-shaped fractal.
    • Used to study chaos theory and weather prediction.
    • Properties: Non-repeating, fractal dimension, sensitive to initial conditions.

Now Let’s Compute Some Complexity

Fractals are more than just pretty pictures. They help us understand complex systems, from the growth of galaxies to the structure of the internet. For programmers, fractals are a playground for exploring algorithms, visualization, and interactivity. Let’s now create an interactive Mandelbrot set visualization using Python and Bokeh, where you can tweak parameters like zoom and iterations to explore its infinite complexity.

The Mandelbrot set is a perfect fractal to visualize because of its striking patterns and computational simplicity. We’ll use Python with the Bokeh library to generate an interactive plot, allowing users to adjust the zoom level and the number of iterations to see how the fractal changes.

Prerequisites

  • Install required libraries: pip install numpy bokeh
  • Basic understanding of Python and complex numbers.

Code Overview

The code below does the following:

  1. Computes the Mandelbrot set by iterating the function z_{n+1} = z_n^2 + c for each point in a grid.
  2. Colors points based on how quickly they escape to infinity (for points outside the set) or marks them black (for points inside).
  3. Uses Bokeh to create a static plot with static parameters.
  4. Uses Bokeh to create a static plot.
  5. Displays the fractal as an image with a color palette.
import numpy as np
from bokeh.plotting import figure, show
from bokeh.io import output_notebook
from bokeh.models import LogColorMapper, LinearColorMapper
from bokeh.palettes import Viridis256
import warnings
warnings.filterwarnings('ignore')

# Enable Bokeh output in Jupyter Notebook
output_notebook()

# Parameters for the Mandelbrot set
width, height = 800, 600  # Image dimensions
x_min, x_max = -2.0, 1.0  # X-axis range
y_min, y_max = -1.5, 1.5  # Y-axis range
max_iter = 100  # Maximum iterations for divergence check

# Create coordinate arrays
x = np.linspace(x_min, x_max, width)
y = np.linspace(y_min, y_max, height)
X, Y = np.meshgrid(x, y)
C = X + 1j * Y  # Complex plane

# Initialize arrays for iteration counts and output
Z = np.zeros_like(C)
output = np.zeros_like(C, dtype=float)

# Compute Mandelbrot set
for i in range(max_iter):
    mask = np.abs(Z) <= 2
    Z[mask] = Z[mask] * Z[mask] + C[mask]
    output += mask

# Normalize output for coloring
output = np.log1p(output)

# Create Bokeh plot
p = figure(width=width, height=height, x_range=(x_min, x_max), y_range=(y_min, y_max),
           title="Mandelbrot Fractal", toolbar_location="above")

# Use a color mapper for visualization
color_mapper = LogColorMapper(palette=Viridis256, low=output.min(), high=output.max())
p.image(image=[output], x=x_min, y=y_min, dw=x_max - x_min, dh=y_max - y_min,
        color_mapper=color_mapper)

# Display the plot
show(p)

When you run in in-line you should see the following:

BokehJS 3.6.0 successfully loaded.

Mandlebrot Fractal with Static Parameters

Now let’s do something a little more interesting and add some adjustable parameters for pan and zoom to explore the complex plane space. i’ll break the sections down as we have to add a JavaScript callback routine due to some funkiness with Bokeh and Jupyter Notebooks.

Ok so first of all, let’s break down the imports:

Ok import Libraries:

  • numpy for numerical computations (e.g., creating arrays for the complex plane).
  • bokeh modules for plotting (figure, show), notebook output (output_notebook), color mapping (LogColorMapper), JavaScript callbacks (CustomJS), layouts (column), and sliders (Slider).
  • The above uses Viridis256 for a color palette for visualizing the fractal. FWIW Bokeh provides us with Matplotlib color palettes. There are 5 types of Matplotlib color palettes Magma, Inferno, Plasma, Viridis, Cividis. Magma is my favorite.

Warnings: Suppressed to avoid clutter from Bokeh or NumPy. (i know treat all warnings as errors).

import numpy as np
from bokeh.plotting import figure, show
from bokeh.io import output_notebook
from bokeh.models import LogColorMapper, ColumnDataSource, CustomJS
from bokeh.palettes import Viridis256
from bokeh.layouts import column
from bokeh.models.widgets import Slider
import warnings
warnings.filterwarnings('ignore')

Next, and this is crucial. The following line configures Bokeh to render plots inline in the Jupyter Notebook:

output_notebook()

Initialize the MandleBrot Set Parameters where:

  • width, height: Dimensions of the output image (800×600 pixels).
  • initial_x_min, initial_x_max: Range for the real part of complex numbers (-2.0 to 1.0).
  • initial_y_min, initial_y_max: Range for the imaginary part of complex numbers (-1.5 to 1.5).
  • max_iter: Maximum number of iterations to determine if a point is in the Mandelbrot set (controls detail).

These ranges define the initial view of the complex plane, centered around the Mandelbrot set’s most interesting region.

width, height = 800, 600
initial_x_min, initial_x_max = -2.0, 1.0
initial_y_min, initial_y_max = -1.5, 1.5
max_iter = 100

Okay, now we’re getting somewhere. Onto the meat of the code, as it were. The computation function:

Purpose: Computes the Mandelbrot set for a given region of the complex plane.Steps:

  • Create arrays x and y for real and imaginary parts using np.linspace.
  • Form a 2D grid with np.meshgrid and combine into a complex array C = X + 1j * Y.
  • Initialize Z (iteration values) and output (iteration counts) as zero arrays.
  • Iterate up to max_iter times:
    • mask = np.abs(Z) <= 2: Identifies points that haven’t diverged (magnitude ≤ 2).
    • Update Z for non-diverged points using the Mandelbrot formula: Z = Z^2 + C
    • Increment output for points still in the set (via mask).
  • Apply np.log1p(output) to smooth the color gradient for visualization. This took me a while to be honest.

Output: A 2D array where each value represents the (log-transformed) number of iterations before divergence, used for coloring.

def compute_mandelbrot(x_min, x_max, y_min, y_max, width, height, max_iter):
    x = np.linspace(x_min, x_max, width)
    y = np.linspace(y_min, y_max, height)
    X, Y = np.meshgrid(x, y)
    C = X + 1j * Y
    Z = np.zeros_like(C)
    output = np.zeros_like(C, dtype=float)
    for i in range(max_iter):
        mask = np.abs(Z) <= 2
        Z[mask] = Z[mask] * Z[mask] + C[mask]
        output += mask
    return np.log1p(output)

Let’s call the function where we compute the Mandelbrot set for the initial view (-2.0 to 1.0 real, -1.5 to 1.5 imaginary).

output = compute_mandelbrot(initial_x_min, initial_x_max, initial_y_min, initial_y_max, width, height, max_iter)

Now we are going to set up the eye candy with bokeh.

Figure: Creates a Bokeh plot with the specified dimensions and initial ranges.

  • x_range and y_range set the plot’s axes to match the complex plane’s real and imaginary parts.
  • toolbar_location=”above” adds zoom and pan tools above the plot. (if anyone has any idea, i couldn’t get this to work correctly)
  • Color Mapper: Maps iteration counts to colors using Viridis256 (a perceptually uniform palette). LogColorMapper: ensures smooth color transitions.
  • Image: Renders the output array as an image:
  • dw, dh: Width and height in data coordinates.
p = figure(width=width, height=height, x_range=(initial_x_min, initial_x_max), y_range=(initial_y_min, initial_y_max),
           title="Interactive Mandelbrot Fractal", toolbar_location="above")
color_mapper = LogColorMapper(palette=Viridis256, low=output.min(), high=output.max())
image = p.image(image=[output], x=initial_x_min, y=initial_y_min, dw=initial_x_max - initial_x_min, dh=initial_y_max - initial_y_max, color_mapper=color_mapper)

Now let’s add some interactivity where:

Sliders:

step: Controls slider precision (0.1 for smooth adjustments).

x_pan_slider: Adjusts the center of the real axis (range: -2.0 to 1.0, default 0).

y_pan_slider: Adjusts the center of the imaginary axis (range: -1.5 to 1.5, default 0).

zoom_slider: Scales the view (range: 0.1 to 3.0, default 1.0; smaller values zoom in, larger zoom out).

x_pan_slider = Slider(start=-2.0, end=1.0, value=0, step=0.1, title="X Pan")
y_pan_slider = Slider(start=-1.5, end=1.5, value=0, step=0.1, title="Y Pan")
zoom_slider = Slider(start=0.1, end=3.0, value=1.0, step=0.1, title="Zoom")

Ok, now here is what took me the most time, and I had to research it because, well, because i must be dense. We need to add a JavaScript Callback for Slider Updates. This code updates the plot’s x and y ranges when sliders change, without recomputing the fractal (for performance). For reference: Javascript Callbacks In Bokeh.

  • zoom_factor: Scales the view (1.0 = original size, <1 zooms in, >1 zooms out).
  • x_center: Shifts the real axis center by x_pan.value from the initial center.
  • y_center: Shifts the imaginary axis center by y_pan.value.
  • x_width, y_height: Scale the original ranges by zoom_factor.
  • Updates p.x_range and p.y_range to reposition and resize the view.

The callback triggers whenever any slider’s value changes.

callback = CustomJS(args=dict(p=p, x_pan=x_pan_slider, y_pan=y_pan_slider, zoom=zoom_slider,
                             initial_x_min=initial_x_min, initial_x_max=initial_x_max,
                             initial_y_min=initial_y_min, initial_y_max=initial_y_max),
                    code="""
    const zoom_factor = zoom.value;
    const x_center = initial_x_min + (initial_x_max - initial_x_min) / 2 + x_pan.value;
    const y_center = initial_y_min + (initial_y_max - initial_y_min) / 2 + y_pan.value;
    const x_width = (initial_x_max - initial_x_min) * zoom_factor;
    const y_height = (initial_y_max - initial_y_min) * zoom_factor;
    p.x_range.start = x_center - x_width / 2;
    p.x_range.end = x_center + x_width / 2;
    p.y_range.start = y_center - y_height / 2;
    p.y_range.end = y_center + y_height / 2;
""")
x_pan_slider.js_on_change('value', callback)
y_pan_slider.js_on_change('value', callback)
zoom_slider.js_on_change('value', callback)

Ok, here is a long explanation which is important for the layout and display to understand what is happening computationally fully:

  • Layout: Arranges the plot and sliders vertically using column.
  • Display: Renders the interactive plot in the notebook.

X and Y Axis Labels and Complex Numbers

The x and y axes of the plot represent the real and imaginary parts of complex numbers in the plane where the Mandelbrot set is defined.

  • X-Axis (Real Part):
    • Label: Implicitly represents the real component of a complex number c=a+bi.
    • Range: Initially spans -2.0 to 1.0 (covering the Mandelbrot set’s primary region).
    • Interpretation: Each x-coordinate corresponds to the real part a of a complex number c. For example, x=−1.5 x = -1.5 x=−1.5 corresponds to a complex number with real part -1.5 (e.g., c=−1.5+bi).
    • Role in Mandelbrot: The real part, combined with the imaginary part, defines the constant c in the iterative formula z_{n+1} = z_n^2 + c.
  • Y-Axis (Imaginary Part):
    • Label: Implicitly represents the imaginary component of a complex number c=a+bi.
    • Range: Initially spans -1.5 to 1.5 (symmetric to capture the set’s structure).
    • Interpretation: Each y-coordinate corresponds to the imaginary part b (scaled by i). For example, y=0.5 y = 0.5 y=0.5 corresponds to a complex number with imaginary part 0.5i (e.g., c=a+0.5i).
    • Role in Mandelbrot: The imaginary part contributes to c, affecting the iterative behavior.
  • Complex Plane:
    • Each pixel in the plot corresponds to a complex number c=x+yi, where x is the x-coordinate (real part) and y is the y-coordinate (imaginary part).
    • The Mandelbrot set consists of points c where the sequence z_0 = 0 , z_{n+1} = z_n^2 + c remains bounded (doesn’t diverge to infinity).
    • The color at each pixel reflects how quickly the sequence diverges (or if it doesn’t, it’s typically black).
  • Slider Effects:
    • X Pan: Shifts the real part center, moving the view left or right in the complex plane.
    • Y Pan: Shifts the imaginary part center, moving the view up or down.
    • Zoom: Scales both real and imaginary ranges, zooming in (smaller zoom_factor) or out (larger zoom_factor). Zooming in reveals finer details of the fractal’s boundary.
layout = column(p, x_pan_slider, y_pan_slider, zoom_slider)
show(layout)

A couple of notes on general performance:

  • Performance: The code uses a JavaScript callback to update the view without recomputing the fractal, which is fast but limits resolution. For high-zoom levels, you’d need to recompute the fractal (not implemented here for simplicity).
  • Axis Labels: Bokeh doesn’t explicitly label axes as “Real Part” or “Imaginary Part,” but the numerical values correspond to these components. You could add explicit labels using p.xaxis.axis_label = “Real Part” and p.yaxis.axis_label = “Imaginary Part” if desired.
  • Fractal Detail: The fixed max_iter=100 limits detail at high zoom. For deeper zooms, max_iter should increase, and the fractal should be recomputed. Try adding a timer() function to check compute times.

Ok here is the full listing you can copypasta this into a notebook and run it as is. i suppose i could add gist.

import numpy as np
from bokeh.plotting import figure, show
from bokeh.io import output_notebook
from bokeh.models import LogColorMapper, ColumnDataSource, CustomJS
from bokeh.palettes import Viridis256
from bokeh.layouts import column
from bokeh.models.widgets import Slider
import warnings
warnings.filterwarnings('ignore')

# Enable Bokeh output in Jupyter Notebook
output_notebook()

# Parameters for the Mandelbrot set
width, height = 800, 600  # Image dimensions
initial_x_min, initial_x_max = -2.0, 1.0  # Initial X-axis range
initial_y_min, initial_y_max = -1.5, 1.5  # Initial Y-axis range
max_iter = 100  # Maximum iterations for divergence check

# Function to compute Mandelbrot set
def compute_mandelbrot(x_min, x_max, y_min, y_max, width, height, max_iter):
    x = np.linspace(x_min, x_max, width)
    y = np.linspace(y_min, y_max, height)
    X, Y = np.meshgrid(x, y)
    C = X + 1j * Y  # Complex plane

    Z = np.zeros_like(C)
    output = np.zeros_like(C, dtype=float)

    for i in range(max_iter):
        mask = np.abs(Z) <= 2
        Z[mask] = Z[mask] * Z[mask] + C[mask]
        output += mask

    return np.log1p(output)

# Initial Mandelbrot computation
output = compute_mandelbrot(initial_x_min, initial_x_max, initial_y_min, initial_y_max, width, height, max_iter)

# Create Bokeh plot
p = figure(width=width, height=height, x_range=(initial_x_min, initial_x_max), y_range=(initial_y_min, initial_y_max),
           title="Interactive Mandelbrot Fractal", toolbar_location="above")

# Use a color mapper for visualization
color_mapper = LogColorMapper(palette=Viridis256, low=output.min(), high=output.max())
image = p.image(image=[output], x=initial_x_min, y=initial_y_min, dw=initial_x_max - initial_x_min,
                dh=initial_y_max - initial_y_min, color_mapper=color_mapper)

# Create sliders for panning and zooming
x_pan_slider = Slider(start=-2.0, end=1.0, value=0, step=0.1, title="X Pan")
y_pan_slider = Slider(start=-1.5, end=1.5, value=0, step=0.1, title="Y Pan")
zoom_slider = Slider(start=0.1, end=3.0, value=1.0, step=0.1, title="Zoom")

# JavaScript callback to update plot ranges
callback = CustomJS(args=dict(p=p, x_pan=x_pan_slider, y_pan=y_pan_slider, zoom=zoom_slider,
                             initial_x_min=initial_x_min, initial_x_max=initial_x_max,
                             initial_y_min=initial_y_min, initial_y_max=initial_y_max),
                    code="""
    const zoom_factor = zoom.value;
    const x_center = initial_x_min + (initial_x_max - initial_x_min) / 2 + x_pan.value;
    const y_center = initial_y_min + (initial_y_max - initial_y_min) / 2 + y_pan.value;
    const x_width = (initial_x_max - initial_x_min) * zoom_factor;
    const y_height = (initial_y_max - initial_y_min) * zoom_factor;
    
    p.x_range.start = x_center - x_width / 2;
    p.x_range.end = x_center + x_width / 2;
    p.y_range.start = y_center - y_height / 2;
    p.y_range.end = y_center + y_height / 2;
""")

# Attach callback to sliders
x_pan_slider.js_on_change('value', callback)
y_pan_slider.js_on_change('value', callback)
zoom_slider.js_on_change('value', callback)

# Layout the plot and sliders
layout = column(p, x_pan_slider, y_pan_slider, zoom_slider)

# Display the layout
show(layout)

Here is the output screen shot: (NOTE i changed it to Magma256)

Mandlebrot Fractal with Interactive Bokeh Sliders.

So there ya have it. A little lab for fractals. Maybe i’ll extend this code and commit it. As a matter of fact, i should push all the SnakeByte code over the years. That said concerning the subject of Fractals there is a much deeper and longer discussion to be had relating fractals, the golden ratio ϕ=1+ϕ1​ and the Fibonacci sequences (A sequence where each number is the sum of the two preceding ones: 0, 1, 1, 2, 3, 5, 8, 13, 21…) are deeply interconnected through mathematical patterns and structures that appear in nature, art, and geometry. The very essence of our lives.

Until Then.

#iwishyouwater <- Pavones,CR Longest Left in the Northern Hemisphere. i love it there.

Ted ℂ. Tanner Jr. (@tctjr) / X

MUZAK To Blog By: Arvo Pärt, Für Anna Maria, Spiegel im Spiegel (Mirror In Mirror) very fractal song. it is gorgeous.

References:

Here is a very short list of books on the subject. Ping me as I have several from philosophical to mathematical on said subject.

[1] Fractals for the Classroom: Strategic Activities Volume One by Peitgen, HeinzOtto; Jürgens, Hartmut; Saupe, Dietmar by Springer

Fractals for the Classroom: Strategic Activities Volume One by Peitgen, HeinzOtto; Jürgens, Hartmut; Saupe, Dietmar by Springer

Good Introduction Text. Has some fun experiments like creating fractals with video feedback.

[2] Fractals and Chaos: The Mandelbrot Set and Beyond

Fractals and Chaos: The Mandelbrot Set and Beyond

[3] Introduction to Dynamic Systems: Theory, Models, and Applications by Luenberger, David G. by Wiley

Introduction to Dynamic Systems: Theory, Models, and Applications by Luenberger, David G. by Wiley

Great book on stability and things like Lorentz attractors and Lyapunov functions.

[4] The Fractal Geometry of Nature by Benoit B. Mandelbrot by W. H. Freeman & Co.

The Fractal Geometry of Nature by Benoit B. Mandelbrot by W. H. Freeman & Co.

The book that started it all. I was lucky enough to see a lecture by him. Astounding.

[5] Nonlinear Dynamics and Chaos: With Applications to Physics, Biology, Chemistry, and Engineering By Steven H Strogatz

Nonlinear Dynamics and Chaos: With Applications to Physics, Biology, Chemistry, and Engineering By Steven H Strogatz

This is a tough slog and not for the faint of heart.

[6] Complexity: A Guided Tour by Mitchell, Melanie

Complexity: A Guided Tour by Mitchell, Melanie by OUP Us

Professor Mitchell wrote a great accessible book. i took her complexity class at the Sante Fe Institute.

[7]

Leave a Reply

Your email address will not be published. Required fields are marked *