Home       About Me       Contact       Downloads       Update    Part 78   

Part 77: WebGL

March 23, 2013

Florian Bösch has been recommending WebGL for development recently, and has several blog posts dedicated to it, starting with Why You Should Use WebGL. We've exchanged some email about it (I've been a bit critical), but I decided to see if I could use it. Over the past couple of weeks, I've coded a Javascript version of my Milligram 3D library and converted a couple of the demos.

Rotating Cube Demo

Solar System Demo

If you have the right browser, you should be able to just click on these and run them. No installation procedure! Use WASD to move (or click the on-screen buttons at bottom-left), and mouse-drag to look around (or touch the screen.)

On my desktop machines, I've tried the demos under Chrome, on Windows, Linux and Mac, and under Firefox on Windows. Safari on the Mac won't run the demos because the performance timer is missing from Javascript (sigh.) Firefox has some issues with the way I'm grabbing the cursor (wants the app to be fullscreen first.)

I also bought a Google Nexus 10 tablet and the demo runs there with Chrome. I've thrown together a very crude touch interface. It sort of works! More on that below.

Note: To use WebGL on Android Chrome, you have to install the latest Chrome from the store, then go to "chrome://flags/" and scroll down a page or two. You'll see an "Enable WebGL" option. Click on it. See Enable WebGL for details.

I still have a lot to do before I can make up my mind on this, but here are the advantages and disadvantages so far.


Ease of distribution

By far the biggest advantage is that users just click to run the demo and don't have to install anything. Since you are risking your system whenever you install an executable off the net, this will undoubtedly get me some users who would never download a demo from some unknown site.

Being a web page also means there are no old versions out there. I can upgrade the code and be sure everyone is using the latest.

And finally, I don't have to put up with some vendor-specific "App Store" which fusses over whether my demo is appropriate for children or competes with a vendor product. I don't have to pay to get into the store or wait for a review of each new release.


Of course, rather than depending on people installing my code, I now have to depend on them having a WebGL-capable browser. But even if I have to tell people "install Chrome to run this", that's a lot safer for users than "install my executable." Since there are versions of Chrome for Windows, Linux, Mac and Android, my WebGL demos can run anywhere the C++ versions do.

Florian's statistics show that 74% of desktop users and 6% of mobile users have WebGL browsers as of February 2, 2013. I would expect the percentage to increase in the future, although it's frustrating that neither Apple or Microsoft are really supporting the standard.

The support on Android is also attractive. To get on tablets otherwise means developing native Android and iOS versions of the demo. I don't really want to do that, since I'm spending too much time on platform issues as it is.

Consistency across platforms

Coding to WebGL and Javascript, I have a single version of the source which runs on all platforms. I also don't have a complex build process that creates executables for three different operating systems (which I then test on half a dozen configurations.) That would be very nice!

Florian points to the large set of WebGL conformance tests, designed to make sure programs run everywhere. This is a welcome change from OpenGL, which has been very frustrating for me. The WebGL implementation even seems to check shader code more thoroughly than OpenGL.

WebGL is implemented on top of both OpenGL and DirectX. This means I shouldn't have issues with out of date or limited function OpenGL drivers. And in fact, one of my machines (the Lenovo laptop with integrated Intel graphics that was frustrating me in Part 59) proves this.

That machine does not run the C++ version of the Solar System demo, even though there is supposedly a good OpenGL driver. The planet, rendered in the shader, just shows as a black rectangle. But Chrome on that machine runs the WebGL version just fine, even though the shader for the planet is identical. I assume it's converting the shader to a DirectX implementation.

Finally, if I do get a report of bad graphics, I can point people to the conformance tests or just say "sorry, I need Chrome", rather than trying to debug some odd graphics issue without a machine to test on.


But despite all the good points, there are disadvantages...

Javascript is a nuisance

The Javascript debugger under Chrome is pretty usable -- it lets me view the source, set breakpoints and walk through the code as in Visual Studio. You can see the stack, hover over variables to get their values, etc. But Javascript as a language leaves a lot to be desired.

The problem is the language has no types, and converts anything to anything. The parser doesn't find many errors, and neither does the runtime. For example, I have a method to set the light direction:

// set light direction
mgDisplayServices.prototype.setLightDir = function(
  this.lightDir.x = pt.x;
  this.lightDir.y = pt.y;
  this.lightDir.z = pt.z;
But suppose I call this as
setLightDir(0, 1, 0)
I'm sending this x,y, and z, when it expects a point. In C++, I would get two compiler errors: wrong number of arguments and wrong type of argument 1. Javascript just tries to run it.

When the code tries to execute "pt.x", it has an integer instead of a point, and ".x" is "undefined." So it sets the three coordinates of lightDir to undefined. Then normalize tries to divide the coordinates by the length, and ends up setting them all to NaN (Not a Number.)

Later in the code, I try to convert the light direction to eye coordinates and send it to the shader in WebGL:

var eyeLightDir = this.eyeMatrix.mapPt(this.lightDir);
this.gl.uniform3f(index, eyeLightDir.x, eyeLightDir.y, eyeLightDir.z);

But, since lightDir is now "not a number", so is the transformed point (Javascript just keeps going, despite all the numbers being invalid). uniform3f converts "NaN" to zero, and I've just sent an invalid vector to the shader. The shader is also not allowed to throw errors, so it will just do a bit of invalid arithmetic and my resulting scene is dark (no light.)

This is really a pain in the neck to debug! So far, I've just been converting code that I know works. Writing something from scratch is going to be a real nuisance. You could read over the original call to setLightDir a hundred times and not realize it's wrong.

Javascript is slow

I'm not sure how much of a performance penalty I'm going to pay for Javascript. There are a few issues:

  • How slow is basic arithmetic? If the compiler recognizes these cases and converts them into sensible machine language, it could be fast. However, the lack of types could keep it from recognizing reusable blocks. After all, "a+b" means something completely different for integers vs. strings. If basic operations are slow, there's no way, even with optimization, to write a fast program. Interpreters can kill your performance by a factor of 20.

  • On Android tablets, does Javascript compile to ARM machine language, to Android VM opcodes, or is it interpreted? Tablets are the slow devices, and a completely interpreted Javascript would be deadly to performance there. All I know at this point is that both demos run at 60fps, but they don't do much.

  • What about multiple threads? All the CPUs I'm dealing with are multicore at this point, so not having threads means leaving all the other processors idle. That would be a huge performance hit compared to a C++ implementation.

WebGL is OpenGL 2.1

WebGL shaders are from OpenGL ES 2.0 (the mobile version of OpenGL), which is roughly comparable to OpenGL 2.1. There are no texture arrays or instancing, which is a real nuisance.

In this post, Florian additionally lists lack of support for multiple render targets, lack of texture lookup in vertex shaders and lack of floating point textures. The post is from September 2011, so I'm not sure of the current status of these items.

WebGL is still under development

The WebGL standard is still evolving and there are little variations from browser to browser. On Chrome, I can capture the mouse cursor for a first-person-shooter style of play. On Firefox, I can only do this if I've previously switched the game to full-screen mode.

On Chrome under Windows, I can capture the left and right mouse buttons, but still get the page context menu when I click both buttons quickly in sequence. Under Firefox, I get the menu all the time on a right click. Florian says he can't get the context menu at all (not sure which browser/OS he's running.)

When writing WebGL code, you'll have to code disgusting things like this:

// Provides requestAnimationFrame in a cross browser way.
window.requestAnimFrame = (function() 
    window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.oRequestAnimationFrame ||
    window.msRequestAnimationFrame ||
    function(callback, element) 
      return window.setTimeout(callback, 1000/60);

This is running down through the various names the same "request animation" feature has in each browser, and defaulting to an older way of doing it if none of them is available.

I've also run into the case where the high resolution timer was documented as "window.performance.now()" which works under Chrome, but wants to be "performance.now()" on other browsers, and isn't supported at all under Safari.

Missing Features?

To write a complete game, I also need more features than just graphics. This includes sound support, some way to cache objects locally, and a socket connection back to my server (the WebSocket standard is under development.)

I'd also like to be able to read/write local files with a file dialog, so I could do things like import Minecraft data. I don't think that's possible though.

WebGL So Far

I want to keep exploring WebGL until I make up my mind about it. Currently, I'm thinking of finishing off the "Don't Hit Me" game as a WebGL app. I also want to convert some of the other demos. The tree-generating and landscape demos involve a serious amount of CPU, so perhaps I can get a better idea of Javascript performance from them.

Google Nexus 10

I bought an iPad 2 a couple of years ago, and use it as a tablet/ebook reader, but haven't done any programming for it. I set up the IDE and compiled some of the demos, but it was all kind of a nuisance. By that point in the project, I was already sick of supporting multiple platforms. I also knew that I couldn't really distribute demos without going through the app store process, and that was pointless given the state of the project.

Android seemed more accessible, but when I downloaded the IDE for that, I was disappointed. I'm not sure if there's something I needed to do, but the emulator was so slow that it was unusable even for trivial apps. Developing a graphics-intensive app would have been a joke.

I thought about buying an Android tablet, but most of the NewEgg/Amazon reviews mentioned lots of problems. I looked at the ASUS Transformer and Samsung Galaxy Note, but couldn't really justify spending the money. Florian suggested the Google Nexus 10. It has better hardware specs (especially the display) and was cheaper than the others, so I bought one. It arrived yesterday.

One of the problems mentioned in the Amazon review was the machine spontaneously rebooting. I haven't seen this, but there are times when it suddenly goes idle and you have to unlock the display again. I have no idea what's causing this.

The only big annoyance so far has been that it did not correctly run the two demo pages! My nebula sky was not rendered correctly. I've changed the code to work around this, but there definitely seems to be either a bug or a difference between WebGL on this platform and the desktop. Florian has encouraged me to track it down and build a conformance test case out of the problem. That sounds like a good idea.

Nexus 10 running Solar System demo

I had thought that a finger touch would turn into a mouse button click for Javascript, but this isn't supported. Instead, you get a series of touch events, with multiple touch points. I changed the app code to support a small set of movement buttons on screen and change the view angle with a drag of the finger. It's all very flaky and needs work, but you should be able to use the app on a tablet. Let me know in the comments if any of you get it to run on other devices.

Note: To use WebGL on Android Chrome, you have to install the latest Chrome from the store, then go to "chrome://flags/" and scroll down a page or two. You'll see an "Enable WebGL" option. Click on it. See Enable WebGL for details.

Health Note

Since I've mentioned my health issues a lot the last few months, I guess I should keep you up to date. I've been diagnosed with fairly serious sleep apnea (RDI of 28.9 and AHI of 17.6) and I'm trying to find out what I can do about that.

I'm trying to get back into the project. Hopefully, I can resume posting on a regular schedule.

Home       About Me       Contact       Downloads       Update    Part 78   

blog comments powered by Disqus