I like math, and computer science, and this project was a neat learning experience for me. At the start, I just used it to generate the profile picture that I use for most things, but I kept adding onto it, and now it's a GUI app with very little of the original code.
You can clone the repository and run it with Cargo, or run cargo install --git https://github.com/matt-cornell/fractals to just install the binary. The app doesn't take any command-line arguments, and it should be fairly straight-forward to navigate the GUI.
The Mandelbrot set is the set of all complex
While this already gets some pretty pictures, I add a few extensions:
A Phoenix fractal is an extension to this series with another complex parameter
So we have a single series,
So far, the series has been squaring the previous term, but raising it to a different exponent gives different, but still appealing, results. For integer exponents greater than 2, the Z-plane exhibits a higher degree of rotational symmetry, and the C-plane shows a rotational symmetry that was previously hidden. For negative exponents, the resulting image is wildly different, with bubble-like areas with the same escape time.
As much as I love the math I used in this project, I also think there's a few cool things in the software itself.
GUIs are hard to get right and generally kinda suck, but by using egui, it was fairly easy to get it working. I'm also fairly happy with how I cached images to avoid having to re-render large fractals every frame, since egui is an immediate-mode GUI.
Okay, this bothers me because it should not have taken as long as it did, but it's actually pretty hard to make an editor with egui that can actually scroll. Sure, you can just put a TextEdit component in a ScrollArea, but the surrounding frame scrolls too and I didn't like how that looked. What did I do instead? I rendered the TextEdit without the default frame, then put that in the ScrollArea, and then drew the frame around all of that! It took a few tries, but now there's a seamless editor that looks how it should.
Big images have a lot of pixels, and the CPU doesn't like having to figure out what to put for all of them, even if the load is spread out across all of your cores (in fact, it likes that even less, because it can freeze up your whole system instead of just the one app). The nice thing about this, though, is that the pixels are completely independent of each other! By doing all of my rendering in a fragment shader (which I wrote in GLSL), all of the heavy rendering is done on the GPU, and the increased parallelism makes it run smoothly even for massive images. In fact, I can render larger images with it than what my image viewer can load, so I must be doing something right.
Whenever I make an "explorer"-style app like this, I like to be able to serialize the state for later use so I can pick up where I left off some other time. Using serde_json isn't particularly groundbreaking, but there's some neat work done to make it serialize the colors in the gradient in a way that was human-readable. Sure, {color: [255, 0, 0, 255], stop: 0.5} is readable, but "#ff0000ff@0.5" is just cleaner. That... took longer to implement, especially with useful error messages, than I wanted. But it's done now, so don't think about how long it took!
rfd, which gives me nice file dialogs to load and save files, has an asynchronous API (and the synchronous one would just block the thread, which isn't great). I could've pulled in a separate async runtime library, but that'd just require more code to be asynchronous. Instead, I came up with a brilliant solution: I just need to poll the future for the file dialog, if it exists, every frame! The code for it isn't really the prettiest, but it works better than anything else I wanted to write.
Here's some cool fractals I generated!
This is how it looks with the config I use to generate my standard profile picture:

I call this one "bubbles" because, well, look at it:

My friend's roommate made this, he called it the tthinhg and so shall I:
