I present to you my latest hack: Muon Baryon for JavaScript/WebGL. (Note: requires an HTML5/WebGL browser, a decent graphics card, and the pre-calculation time before the demo actually starts is quite long).
The Original
So, what is Muon Baryon? It’s a Windows 4k demo originally created by Youth Uprising, Ümlaüt Design and Outracks. More precisely, it’s the winner of the Assembly 2009 4k contest (so it’s a bit old – but still one of my absolute favorites). You can find out more here.
The HTML5 Port
Now, Muon Baryon was low hanging fruit for an HTML5 port, since:
- Most of it is written in GLSL, which is available through WebGL.
- The most complicated non-GLSL part was the music synth, and it has already been ported to JavaScript.
So, to begin with I started out converting the remaining C code to JavaScript, which mostly consisted of replacing things like glUseProgram() with gl.useProgram(), etc (a no-brainer). I also had to replace the handy desktop OpenGL call glRect with slightly more bloated vertex buffer code. When trying it out for the first time, nothing worked (black screen)… of course. I suspected the GLSL code and inserted the following lines after compiling the shaders:
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) alert(gl.getShaderInfoLog(shader));
I quickly realized that the original GLSL code was written for the older desktop GLSL 1.x (using ftransform etc), while WebGL uses GLSL ES, which has dropped a few functions. Here are a few things I had to do to convert the GLSL code to GLSL ES:
- Replace ftransform with a “custom” position attribute (quite simple, since the demo didn’t use any transformations).
- Include the line precision highp float; in the fragment shaders.
- Replace the while-loop with a fixed length for-loop, i.e:
while(g<1.){ ... }
becomes:
for(int j=0; j<999; j++){if(g>=1.)break; ... }
Not sure about how good this solution is, but at least Firefox and Chrome do not complain. Another thing I did was to make the shader resolution independent (the original was hard-coded to a certain screen resolution). Other than that, it was quite straight forward work.
The Result
The size of the original Windows demo was 3977 bytes. I did not expect to be able to squeeze the JavaScript port down to 4k, but I hoped to get it below 8k at least. And the final result, after using Google Closure Compiler and CrunchMe to compress the code, is: 5846 bytes!!
The un-packed code can be found here: demo-unpacked.js
…and the (unreadable) packed code can be found here: demo.js
Getting it to run
Ok, I have tried this in Firefox 5 and Chromium 13+ under Ubuntu with an NVIDIA 9600 card (fairly old by now), and it works just fine.
Windows users may find that it will not work by default (seems to be an issue in the ANGLE GLSL->HLSL conversion). To make it work, enable the OpenGL back-end.
In Firefox, go to about:config, filter out “webgl” and change webgl.prefer-native-gl to true.
With Chrome, you have to start the program with an extra argument, like this: chrome.exe –use-gl=desktop.
Awesome stuff!
I get a black screen in Chromium 14 on a couple of Windows 7 machines, both of which run it fine in Firefox 5. The latter have webgl.prefer-native-gl enabled in about:config, and it also works in Chrome 13 on an OS X machine, so I maybe it’s an ANGLE issue?
Ok, will have to look into it more closely. Might be stricter shader validation or something.
Edit: Here’s a link to try when things go wrong: http://muonbaryon.bitsnbites.eu/check.html It should give error messages if the shader(s) fail to compile.
Thanks. That link didn’t make any difference – I still just got a black page and the audio playing – but I managed to find the solution via a bit of Googling.
Running Chrome/Chromium with the argument –use-gl=desktop fixed the problem for me. As far as I can tell, there’s nothing within Chrome’s options to set this within the browser. chrome://gpu does give you a bunch of extra information when this argument is passed, but you wouldn’t really know anything was amiss from the “default” information on that page.
Thanks again for all your great work on this and your other projects!
Doesn’t work for me on Firefox 7. The check link gives music and a blue->black fade, but nothing else.
The normal link doesn’t do anything.
This is absolutely AWESOME and inspiring, thanks for it. It also works on Safari 5.1 !
doesnt seem to work on macbookpro with Radeon X1600 :/
Have you tried both Firefox 5 & Chrome (latest)? Doesn’t work on my old Mac Mini with Intel graphics either.
Yikes. This crashed my Mac, and that doesn’t happen often.
It seems that a few of you are experiencing problems running this demo (I have not been able to reproduce myself). It should be a fairly straight forward case of standard WebGL (a few different shaders and very simple geometry).
I have not found any errors in the code (yet, please report if you do!), so my guess is that the demo is stressing the current WebGL implementations and/or graphics drivers a bit too far (the shaders are probably more complex than the standard case).
In any case, I hope that WebGL will be a good push for having GLSL compliant graphics cards and implementations everywhere pretty soon 😉
uncaught exception: [Exception… “Component returned failure code: 0x8007000e (NS_ERROR_OUT_OF_MEMORY) [nsIDOMHTMLCanvasElement.width]” nsresult: “0x8007000e (NS_ERROR_OUT_OF_MEMORY)” location: “JS frame :: http://muonbaryon.bitsnbites.eu/demo.js :: :: line 1″ data: no]
error on firefox 6/win7
Do you have lots of RAM? The music generation typically eats a few 100 MBs to 1 GB or so (depending on browser, OS, etc).
Doesn’t work on XP Chrome with my old ATI Radeon HD 4600
Only music is playing – which sounds awesome 😀
I did some tests on OS X Snow Leopard :
Chrome 13 : crashes computer
Safari 5.1 : Ok despite some display bugs at the beginning
Firefox 4.0.1 : Ok
Firefox 5.0.1 : Ok
performed on a MacBook C2D 2.4 / NVidia GeForce 9400M (yes, very slooooow 🙂
Doesn’t work on my Win7 x64 with Chrome 13.0.782.112.
The “check” version complains about X3000 shader errors (unexpected token })
That’s very likely a bug in ANGLE, which seems to fail to convert GLSL to HLSL. Try running with the OpenGL back end instead (as described in the article).