diff --git a/Manifest.toml b/Manifest.toml
index ae3e78a..cac0bfb 100644
--- a/Manifest.toml
+++ b/Manifest.toml
@@ -2,7 +2,7 @@
 
 julia_version = "1.11.4"
 manifest_format = "2.0"
-project_hash = "0a18a0873213e0e8660a5eb202a1fa9f1621a652"
+project_hash = "6ff2a6774137725477bae8d2113d6856bc327396"
 
 [[deps.AbstractFFTs]]
 deps = ["LinearAlgebra"]
@@ -1093,6 +1093,11 @@ git-tree-sha1 = "b9039e93773ddcfc828f12aadf7115b4b4d225f5"
 uuid = "b3c3ace0-ae52-54e7-9d0b-2c1406fd6b9d"
 version = "0.3.2"
 
+[[deps.Ranges]]
+git-tree-sha1 = "b5dd6c6cba97d1fa0641797cb08d4fd913c5fcd5"
+uuid = "eb7db99b-64ae-4b81-85c2-3439b6569b78"
+version = "0.1.0"
+
 [[deps.Ratios]]
 deps = ["Requires"]
 git-tree-sha1 = "1342a47bf3260ee108163042310d26f2be5ec90b"
diff --git a/Project.toml b/Project.toml
index 9661941..73bcad6 100644
--- a/Project.toml
+++ b/Project.toml
@@ -7,10 +7,12 @@ version = "0.1.0"
 FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
 Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
 Luxor = "ae8d54c2-7ccd-5906-9d76-62fc9837b5bc"
+Ranges = "eb7db99b-64ae-4b81-85c2-3439b6569b78"
 VideoIO = "d6d074c3-1acf-5d4c-9a43-ef38773959a2"
 
 [compat]
 FileIO = "1.17.0"
 Images = "0.26.2"
 Luxor = "4.2.0"
+Ranges = "0.1.0"
 VideoIO = "1.1.1"
diff --git a/README.md b/README.md
index f19f8ab..5dfe3b9 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,22 @@
 # Boundary Bad Apple!!
 
 Boundary-aware Bad Apple!! animation generation
+
+## Fractal
+
+Usage:
+
+Load an image with `Images`
+
+```julia
+using Images, FileIO
+img = load("examples/frame.png")
+```
+Then generate the fractal using the pseudomandelbrot function
+
+```julia
+include("src/fractal.jl")
+mandel(img, -0.53+0.60im, -0.51+0.62im)
+```
+The coördinates specified are the bottom left and top right, respectively.
+
diff --git a/src/fractal.jl b/src/fractal.jl
new file mode 100644
index 0000000..9165781
--- /dev/null
+++ b/src/fractal.jl
@@ -0,0 +1,64 @@
+using Luxor, Ranges
+include("image.jl")
+
+function sample(m, u, v)
+    width, height = size(m)
+    i = trunc(Int, u * width)
+    j = trunc(Int, v * height)
+    if i <= 0 || i > width || j <= 0 || j > height
+        return 0.
+    else
+        return greyscale(m[i,j])
+    end
+end
+
+function is_stable(m, z1, z2, c)
+    iter = 1000
+    x1 = real(z1)
+    y1 = imag(z1)
+    x2 = real(z2)
+    y2 = imag(z2)
+    u = (real(c) - x1) / (x2 - x1)
+    v = (imag(c) - y1) / (y2 - y1)
+    f = sample(m, u, v)
+    z = c
+    for _ in 1:iter
+        r = abs(z)
+        if r > 4
+            return false
+        end
+        # Sample the image at position z
+        u = (real(z) - x1) / (x2 - x1)
+        v = (imag(z) - y1) / (y2 - y1)
+        f = sample(m, u, v)
+        if f < 0.5
+            z = z^2*0.99 + c
+        else
+            z = z^2 + c
+        end
+    end
+    true
+end
+
+function mandel(m, z1::Complex, z2::Complex)
+    width, height = size(m)
+    buffer = zeros(ARGB32, width, height)
+    for (i, x) in enumerate(range(real(z1), real(z2), width))
+        for (j, y) in enumerate(range(imag(z1), imag(z2), height))
+            t = greyscale(m[i,j])
+            s = !is_stable(m, z1, z2, x + y * im)
+            if t > 0.5
+                if s
+                    buffer[i,j] = colorant"white"
+                else
+                    buffer[i,j] = colorant"blue"
+                end
+            else
+                if s
+                    buffer[i,j] = colorant"red"
+                end
+            end
+        end
+    end
+    return buffer
+end
diff --git a/src/image.jl b/src/image.jl
new file mode 100644
index 0000000..3eaf2f9
--- /dev/null
+++ b/src/image.jl
@@ -0,0 +1,8 @@
+using Images
+
+"""
+Converts image to grayscale
+"""
+function greyscale(c::Images.RGB)::Real
+    return Real(c.r)
+end