A little bit of fun with fractals!
If you don't see anything in the frame above, then either your browser doesn't support WebGL, or your graphics driver is not sufficient for initializing a WebGL context.
The source code is available on GitHub.
There's nothing special about the way each pixel's color is determined. It's colored based on the number of iterations it takes a point to escape the given distance bound (in this case, sqrt(10)) in the Julia set function, just like any other typical fractal viewer.
I decided to have it precompute the color pallet like this:
function generateColors() {
colors = new Uint32Array(MAX_ITERATIONS);
var escapeTime, red, green, blue;
for (var i = 0; i < colors.length; i++) {
escapeTime = i/MAX_ITERATIONS;
red = (-Math.cos(escapeTime*Math.PI) + 1)/2*0xff;
green = Math.sin(escapeTime*Math.PI)*0xff;
blue = (Math.cos(escapeTime*Math.PI) + 1)/2*0xff;
colors[i] = (0xff000000 | red | green << 8 | blue << 16);
}
}
...and here's what the color functions look like on a graph, where blue starts off strong and dies off, green peaks in the middle, and red ends up strong:
The viewer is made up of a few small components:
- A viewport, which finds the rectangle on the complex plane to render based on given translation coordinates, a zoom multiplier, and the current window resolution.
- A bitmap, which holds the most up-to-date image buffer to display via WebGL. Sections of it are continuously overwritten by the renderer.
- A renderer, which calls the Julia set function continuously, limiting each execution time a maximum of 20ms and keeping track of where it left off in the bitmap between each call. It also keeps track of the offset (a complex number to add to a point each time it's squared in the Julia set function).
- A controller, which sets up the head-up display buttons, text, and user input listeners to modify the zoom and center of the viewport and randomly select new offsets for the Julia set function.
Everytime you hit + to zoom in, the zoom of the viewport is multiplied by 1.5. The viewport generates a rectangle every frame by essentially dividing a given starting rectangle's width and height by the zoom.
Because of this setup, if you zoom in far enough, you'll crash into a pixelated mess:
This is due to the floating point numbers having limited precision - in the case of JavaScript, floating point numbers are 64-bit. You may also notice that the viewport's center snaps while panning when you're zoomed in that far.
The graphics are provided by WebGL via the wonderful IvanK.js. The Julia set itself is, however, ironically software rendered - mainly because IvanK.js isn't intended for use alongside low level access to WebGL.. and I wanted to see how fast I could get it to work in pure JavaScript. c:
No fragment shaders were butchered written in the making of this demo.