Writing these tutorials is all good fun, but I decided to take a short break from them today to show something a little more visually interesting that you can do now that WebGL gives web pages access to the graphics hardware: a page that zooms into the Mandelbrot set in realtime, with all of the calculations taking place in a fragment shader. Fragment shaders run on the graphics card itself, so we can get a pretty impressive frame rate even though we completely recalculate the set every time we draw it.
Here’s a video showing what it looks like when run on a browser that supports WebGL:
Click here and you’ll see the live WebGL version, if you’ve got a browser that supports it; here’s how to get one if you don’t.
[UPDATE: looks like not all drivers will support the shader, which is interesting. It's worked on all the desktop machines I've tried it on but doesn't work on the laptop where I have software rendering. I'll be taking a look at that over the weekend.]
[FURTHER UPDATE: Interesting, I was using "%" for modulo, but that's not valid GLSL. The desktop machines' drivers accepted it anyway, but the MESA software renderer used on the laptop was stricter. I've fixed the bug, and it works now on the laptop. Very very slowly.]
The UI is pretty simple — you can enter a “zoom point” in the fields at the bottom, and click the “Reset Zoom” button to start zooming in again with the target you’ve specified.
I won’t explain the details of how the code works here, but if you have been following the tutorial so far then it should be pretty easy to work out from the source, perhaps with reference to the Wikipedia pages for the Mandelbrot set and Hue/Saturation/Value to RGB conversions. If you haven’t been following the tutorial, you might want to check out lesson 1 and lesson 2, or Benjamin DeLillo’s shorter WebGL intro.
The one thing I’m not entirely satisfied with is the fact that it doesn’t quite zoom to where you ask it to because of (I think) Javascript numerical accuracy problems. Oh, and the colour scheme could be improved :-)
Do let me know if you know of any neat tricks for fixing either of those…


I found one compatibility issue is that some GLSL implementations don’t have the overloaded pow(float, int) and you need to use pow(float, float)
e.g
pow(x, 2);
needs to be
pow(x, 2.0);
You probably want to add a “#version 110″ (or 120) pragma to the top of the shader, as the current WebGL implementations pass the shader to the driver as-is. So you get some shader compatibility hell between vendors, hardware, operating systems and driver versions… I think the final implementations will do compatibility wrangling in the browser before passing on the shader, but they’re not there yet.
The software renderer GLSL implementation has some weird quirks too, reporting them on the MESA bug tracker would be awesome.
@Benjamin — thanks for the warning; turned out it was because I was using “%” as modulo — which is not officially part of the GLSL standard — instead of the “mod” function. Odd that the desktop machines’ graphics cards supported it, but never mind — fixed now.
@Ilmari — that’s good advice, I’ll get into that habit, though I’ll have to work out which version I’m using first :-) Presumably as part of the standardisation process they will work out the minimal spec for WebGL compatibility (eg., do they accept “%” or only mod). Hmmm, I wonder if there are any chances for security problems with just passing shaders to the driver directly? I managed to take crash my machine quite thoroughly by putting a non-terminating loop in a shader (forgot to increment my loop index in a do/while), so if they can’t work out an easy way to block that, people can at least write annoying web pages that crash your machine, even if they can’t break in…
If I do find any bugs in the MESA shader implementation then I’ll definitely report them.
Cheers,
Giles
Note that this demo also uses:
for each (corner in baseCorners)
which is Firefox specific. I believe you can do the same thing with for (… in …) and some dereferencing, which would work in all browsers.
@cmarrin — many thanks again! I’ve fixed the “for each” problem. I’ve not been able to install WebKit on my PC yet, but hopefully with that and the clearDepth fix I’ve also put in, these examples will run on it without problems. Should be able to test it out on my wife’s Mac this evening.
@Benjamin DeLillo just tried this one on a Macbook Air and got loads of type errors like the ones you described; fixed them and it works. Thanks for the heads-up!
Thanks for your lessons.
But I can’t run on my FireFox 3.7a1pre.
OS: win xp professional sp3
graphic card: ati FireGL v3300.
I can run some of demos, like:
http://wakaba.c3.cx/w/puls.html
I hope you could help me. Many thanks.
@Ray — What happens when you look at the demos? Does anything appear in the Firefox Error Console (on the Tools menu)?
For the lesson 1 demo, only a black background and nothing else.
No Error Console.
Thanks for your help.
The Graphic Card Driver date: 2006.2.7 and version is
8.223.0.0
is it too old ?
I have just checked the OpenGL version, it is 2.0 .
I am really new to OpenGL and WebGL. I don’t know what is necessary to support this demo.
Thanks for your attention and your pretty nice lessons.
Ray
Hi Ray,
Glad you like the demo. The OpenGL version should be OK; I’ve commented with some questions that might help debug the problem at http://learningwebgl.com/blog/?p=28&cpage=1#comment-73
Cheers,
Giles
Actually, you should be able to use % for modulus if you go with:
extension GL_EXT_gpu_shader4 : enable
In the first section of your shader.
-Sorry for inconvenience if this response will work like bump somehow.
@Daniel — thanks! I’m a little uncertain about using extensions, though. I’m sure they’ll work in most desktop PCs, but not so sure about mobile devices…
(Oh, and don’t worry — I’m just using Wordpress, comments don’t bump posts up.)
1. You could use colors similar to M-set animation in
http://www.ibiblio.org/e-notes/webgl/webgl.htm
2. There are a few Julia sets animations here (Java based out of dated :-) and one could transfer them into WebGL
3. Unfortunately on Radeon HD 3650 (120 shaders 750MHz) RivaTuner shows me GPU load only 30% in both WebGL animations.
@Evgeny — thanks for the link, those are nice examples. I’d not heard of RivaTuner, sounds like a useful tool. I wonder why neither of our examples are using all of the GPU?
Two simple Julia sets animations
http://www.ibiblio.org/e-notes/webgl/julia.html
http://www.ibiblio.org/e-notes/webgl/julia_de.html