Snapshot.jl
← Notebooks/Fractals and Fractal Art🏝️ 15/15 cells interactive
.jl source
using PlutoUI, PlutoTeachingTools, PlutoImageCoordinatePicker, WasmMakie
using ImageShow, ImageIO, Colors

Fractals: A Mathematical Concept

Hey there πŸ‘‹ Do you remember the first time you ever saw a snowflake? ❄️

Did you also know that if you zoom into a snowflake, you will see the same shape over and over again, no matter how deep you zoom in.

Turns out, many objects have this same property, where they can be split into parts and each tiny part is a copy of the whole. We call them Fractals.

Pretty cool, right? But what's even more cool is that fractals can be defined mathematically following a super simple formula! One famous such set of fractals is called the Julia Set

A Fun Sequence to Work With

Before we get to the fractals, there are a few things we want to talk about.

So, in math, instead of dealing with numbers in a list, we can define a formula that looks like this:

$$z_{n+1} = z_n^2 + 1$$

This is called a sequence. Let's see how it works 😁

Calculating numbers

Step 1: choose an initial value $z_0$ :

Now, we can calculate $z_1$, and $z_2$, and so on:

2
z1 = z0^2 + 1
5
z2 = z1^2 + 1
26
z3 = z2^2 + 1
677
z4 = z3^2 + 1

And so on ..

Basically we calculate the next value by using the same function applied to the last value.

Fun Julia Alert!

Now check out this piece of cool Julia code. Can you guess what it does?

F(z) = z^2 + 1;
function Fⁿ(x, n)
	z = x
	for i in 1:n
		z = F(z)   # apply F, n times in a row
	end
	return z
end;
458330
zβ‚™ = Fⁿ(z0, n)

πŸ’‘ Did you guess what the code does?

Tip: Move both the $n$ and the $z_0$ sliders around if you need help!

Show answer

The Sequence

Now let's put it together!

This code generate the whole sequence up to our chosen n and saves it in a list. If you're interested in how to use Julia methods to write compact code, check it out!

But no worries if you don't understand it, it's not necessary for the rest.

function create_sequence(initial_value, max_n)
	# Build the sequence one step at a time, starting from the initial value
	series = [initial_value]
	z = initial_value
	for i in 2:max_n
		z = F(z)          # next element: apply our function F
		push!(series, z)  # save it in the list
	end
	return series
end;

Change the initial value and n and observe how the series changes!

$z_0 =$ -10

$n =$ 1

sequence = create_sequence(initial_value, max_n)

πŸ‘† Click on the sequence to see all entries.

Divergence!

What happens when we keep doing this for a long time?

For most initial values, our numbers are always increasing and getting bigger and bigger, so they're going to infinity. We say the series diverges.

Fun Functions for a Fun Sequence

Now that you (hopefully) got the gist of that. Let's make it a bit more complicated.

Remember how we chose the formula $z_{n+1} = z_n^2 + 1$ before? Well, we can do this in a more general way as $z_{n+1} = z_n^2 + C$. Before, we had C = 1.

-1
C = -1

Let's define our function again in a more general setting

Fc(z, c) = z^2 + c;

Now let's use our code from above again with our new function this time.

function create_sequence_with_c(C, initial_value, max_n)
	# Same idea as before, but now our function also uses the constant C
	series = [initial_value]
	z = initial_value
	for i in 2:max_n
		z = Fc(z, C)      # next element: apply F with our constant C
		push!(series, z)
	end
	return series
end;

Try setting C = -1. What happens to our C_sequence (if you chance nothing else)?

C_sequence = create_sequence_with_c(C, initial_value_with_c, max_n)

If you noticed that we get a completely new sequence, that's correct!

But, how about if we set initial_value_with_c = 1. What happens? Do you notice anything special about this sequence?

βœ… Answer

In this case, the series doesn't go to infinity but moves back and forth between 0 and -1, and so it doesn't diverge. It oscillates.

PS: The same thing happens if initial_value_with_c = 0

Now, to make things more fun, let's try more values of C and see if the same thing happens again.

To make it even more fun, let's make C a complex number!

If you don't know about complex numbers, that's okay. For now, we can think of C as a 2D point.

πŸ‘‡ Click anywhere to chose a point!

0.9 + 0.4im
c

Again, starting from a fixed initial value, let's watch the size of each term in the sequence. See how it changes completely for even very similar c values? Pretty cool right!

1.0 + 0.0im
new_initial_value = 1.0 + 0.0im
new_sequence = let
	# Track the SIZE (magnitude) of each term β€” that's exactly what tells us
	# whether the sequence stays small (converges) or blows up (diverges).
	# We cap huge values so the list stays readable: a value of 1000000 means
	# "this term blew up".
	zc = new_initial_value
	sizes = Int[]
	for i in 1:15
		m = abs(zc)
		push!(sizes, round(Int, min(m, 1.0e6)))
		m > 1.0e6 && break         # stop once it has clearly diverged
		zc = Fc(zc, c)             # our function from above, now with a complex c
	end
	sizes
end

πŸ‘† Click on the sequence to see all entries.

Convergence?

Let's look at the size of the last term. Is it a small number (convergence), or something very large or NaN (divergence)?

1000000
last(new_sequence)

One last thing

Choose 2 of your favorite colors (ideally they should match each other πŸ˜‰):

Dict{Symbol, Any}(:attributes => Dict{String, Any}("class" => nothing, "style" => "display: flex; flex-direction: row;"), :tag => "div", :children => Any[(Dict{Symbol, Any}(:attributes => Dict{String, Any}("class" => nothing, "style" => "flex: 0 1 49.0%;"), :tag => "div", :children => Any[("<div class=\"markdown\"><p>First color:</p>\n<bond def=\"color1\" unique_id=\"savbduqrotpx\"><select><option value='puiselect-1'>gold</option><option value='puiselect-2'>teal</option><option value='puiselect-3'>crimson</option><option value='puiselect-4'>navy</option><option value='puiselect-5'>magenta</option></select></bond>\n</div>", MIME type text/html)]), MIME type application/vnd.pluto.reactdomelement+object), (Dict{Symbol, Any}(:attributes => Dict{String, Any}("class" => nothing, "style" => "flex: 0 0 2%;"), :tag => "div", :children => Any[]), MIME type application/vnd.pluto.reactdomelement+object), (Dict{Symbol, Any}(:attributes => Dict{String, Any}("class" => nothing, "style" => "flex: 0 1 49.0%;"), :tag => "div", :children => Any[("<div class=\"markdown\"><p>Second color:</p>\n<bond def=\"color2\" unique_id=\"fawsdtlejjgk\"><select><option value='puiselect-1'>green</option><option value='puiselect-2'>purple</option><option value='puiselect-3'>orange</option><option value='puiselect-4'>black</option><option value='puiselect-5'>blue</option></select></bond>\n</div>", MIME type text/html)]), MIME type application/vnd.pluto.reactdomelement+object)])

Construct Fractals

Now we have everything we need to construct a fractal from the Julia set.

Recipe: Julia Set picture

  1. Choose a complex number $c$ (on the left).

  2. We define our favorite sequence $z_{n+1} = z_n^2 + c$

  3. Now, we're going over all the imaginary numbers in a square around $0$, and for each:

    • Use the number as the initial value to compute a sequence.

    • If the sequence converges, we color the point gold.

    • If the sequence diverges, we color the point green.

πŸ‘‡ Drag the sliders to choose the constant $c = c_x + i\,c_y$, zoom, and detail!

$c_x =$ -0.04

$c_y =$ 0.72

zoom 1 Β· detail 60

Bonus: the Mandelbrot set

The Julia set above fixes one constant $c$ and sweeps the starting point $z_0$ across the plane. The Mandelbrot set does the opposite: it starts every pixel at $z_0 = 0$ and uses the pixel's own coordinate as $c$. The result is the single most famous fractal in mathematics β€” and the same simple loop $z \\leftarrow z^2 + c$ draws it.

Pan with the centre sliders and zoom in to find miniature copies of the whole shape hiding along its edge.

centre $x$ -0.5

centre $y$ -0.01

zoom 1 Β· detail 60

If you're interested to dive deep into the code, take a look below, it's so simple and straighforward and yet we can create so many beautiful things with it!

fractal_figure (generic function with 1 method)

Appendix

escape_color (generic function with 1 method)
ComplexNumberPicker (generic function with 1 method)
SVG
UInt8[0x3c, 0x73, 0x76, 0x67, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x32, 0x30, 0x30, 0x30, 0x2f, 0x73, 0x76, 0x67, 0x22, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3d, 0x22, 0x33, 0x30, 0x30, 0x22, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3d, 0x22, 0x33, 0x30, 0x30, 0x22, 0x20, 0x76, 0x69, 0x65, 0x77, 0x42, 0x6f, 0x78, 0x3d, 0x22, 0x30, 0x20, 0x30, 0x20, 0x33, 0x30, 0x30, 0x20, 0x33, 0x30, 0x30, 0x22, 0x20, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x3d, 0x22, 0x6d, 0x61, 0x78, 0x2d, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x31, 0x30, 0x30, 0x25, 0x3b, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x61, 0x75, 0x74, 0x6f, 0x3b, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x22, 0x3e, 0x0a, 0x0a, 0x3c, 0x64, 0x65, 0x66, 0x73, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x74, 0x72, 0x69, 0x61, 0x6e, 0x67, 0x6c, 0x65, 0x22, 0x20, 0x76, 0x69, 0x65, 0x77, 0x42, 0x6f, 0x78, 0x3d, 0x22, 0x30, 0x20, 0x30, 0x20, 0x31, 0x30, 0x20, 0x31, 0x30, 0x22, 0x20, 0x72, 0x65, 0x66, 0x58, 0x3d, 0x22, 0x31, 0x22, 0x20, 0x72, 0x65, 0x66, 0x59, 0x3d, 0x22, 0x35, 0x22, 0x20, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x3d, 0x22, 0x73, 0x74, 0x72, 0x6f, 0x6b, 0x65, 0x57, 0x69, 0x64, 0x74, 0x68, 0x22, 0x20, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x57, 0x69, 0x64, 0x74, 0x68, 0x3d, 0x22, 0x31, 0x30, 0x22, 0x20, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3d, 0x22, 0x31, 0x30, 0x22, 0x20, 0x6f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x61, 0x75, 0x74, 0x6f, 0x2d, 0x73, 0x74, 0x61, 0x72, 0x74, 0x2d, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x70, 0x61, 0x74, 0x68, 0x20, 0x64, 0x3d, 0x22, 0x4d, 0x20, 0x30, 0x20, 0x30, 0x20, 0x4c, 0x20, 0x31, 0x30, 0x20, 0x35, 0x20, 0x4c, 0x20, 0x30, 0x20, 0x31, 0x30, 0x20, 0x7a, 0x22, 0x20, 0x66, 0x69, 0x6c, 0x6c, 0x3d, 0x22, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x3e, 0x3c, 0x2f, 0x70, 0x61, 0x74, 0x68, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x2f, 0x64, 0x65, 0x66, 0x73, 0x3e, 0x0a, 0x0a, 0x3c, 0x72, 0x65, 0x63, 0x74, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3d, 0x22, 0x33, 0x30, 0x30, 0x22, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3d, 0x22, 0x33, 0x30, 0x30, 0x22, 0x20, 0x66, 0x69, 0x6c, 0x6c, 0x3d, 0x22, 0x77, 0x68, 0x69, 0x74, 0x65, 0x22, 0x20, 0x72, 0x78, 0x3d, 0x22, 0x31, 0x65, 0x6d, 0x22, 0x2f, 0x3e, 0x0a, 0x0a, 0x3c, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x78, 0x31, 0x3d, 0x22, 0x31, 0x30, 0x22, 0x20, 0x78, 0x32, 0x3d, 0x22, 0x32, 0x39, 0x30, 0x22, 0x20, 0x79, 0x31, 0x3d, 0x22, 0x31, 0x35, 0x30, 0x22, 0x20, 0x79, 0x32, 0x3d, 0x22, 0x31, 0x35, 0x30, 0x22, 0x20, 0x73, 0x74, 0x72, 0x6f, 0x6b, 0x65, 0x3d, 0x22, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x20, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x2d, 0x73, 0x74, 0x61, 0x72, 0x74, 0x3d, 0x22, 0x75, 0x72, 0x6c, 0x28, 0x23, 0x74, 0x72, 0x69, 0x61, 0x6e, 0x67, 0x6c, 0x65, 0x29, 0x22, 0x20, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x2d, 0x65, 0x6e, 0x64, 0x3d, 0x22, 0x75, 0x72, 0x6c, 0x28, 0x23, 0x74, 0x72, 0x69, 0x61, 0x6e, 0x67, 0x6c, 0x65, 0x29, 0x22, 0x3e, 0x3c, 0x2f, 0x6c, 0x69, 0x6e, 0x65, 0x3e, 0x0a, 0x0a, 0x3c, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x79, 0x31, 0x3d, 0x22, 0x31, 0x30, 0x22, 0x20, 0x79, 0x32, 0x3d, 0x22, 0x32, 0x39, 0x30, 0x22, 0x20, 0x78, 0x31, 0x3d, 0x22, 0x31, 0x35, 0x30, 0x22, 0x20, 0x78, 0x32, 0x3d, 0x22, 0x31, 0x35, 0x30, 0x22, 0x20, 0x73, 0x74, 0x72, 0x6f, 0x6b, 0x65, 0x3d, 0x22, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x20, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x2d, 0x73, 0x74, 0x61, 0x72, 0x74, 0x3d, 0x22, 0x75, 0x72, 0x6c, 0x28, 0x23, 0x74, 0x72, 0x69, 0x61, 0x6e, 0x67, 0x6c, 0x65, 0x29, 0x22

Pluto notebooks as lean Therapy components β€” interactive WebAssembly, no server.