Your WebGL aiders
As you may already know, JavaScript is great for creating immersive web experiences. It’s mainly due to a wide selection of web APIs allowing you to interact with even some low-level features of the device. One of which is most likely well-known to you - WebGL. WebGL is an API used to create advanced interactive visual effects without any plugins right in your browser. Its first version is based on OpenGL ES 2.0 - graphics API for high-performance 2D and 3D GPU-accelerated visuals. Because of that, it is a great choice for things like games, requiring fast response times. It truly smashes Canvas API (2D graphics only) when it comes to speed and its second version (based on OpenGL ES 3.0) only confirms that statement.
Quick look
Now, everything looks awesome on the paper, so why many people don’t recommend and/or haven’t ever touch any kind of bare-bones WebGL code? Well, that’s because of its API, here, take a look.
function createShader(gl, type, source) {
var shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (success) {
return shader;
}
gl.deleteShader(shader);
}
function createProgram(gl, vertexShader, fragmentShader) {
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
var success = gl.getProgramParameter(program, gl.LINK_STATUS);
if (success) {
return program;
}
gl.deleteProgram(program);
}
function main() {
var canvas = document.getElementById("c");
var gl = canvas.getContext("webgl");
if (!gl) {
return;
}
var vertexShaderSource = document.getElementById("2d-vertex-shader").text;
var fragmentShaderSource = document.getElementById("2d-fragment-shader").text;
var vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
var program = createProgram(gl, vertexShader, fragmentShader);
var positionAttributeLocation = gl.getAttribLocation(program, "a_position");
var positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
var positions = [
0, 0,
0, 0.5,
0.7, 0,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
webglUtils.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
gl.enableVertexAttribArray(positionAttributeLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
var size = 2;
var type = gl.FLOAT;
var normalize = false;
var stride = 0;
var offset = 0;
gl.vertexAttribPointer(
positionAttributeLocation, size, type, normalize, stride, offset);
var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 3;
gl.drawArrays(primitiveType, offset, count);
}
main();
The code above is taken from WebGL fundamentals which BTW is a great source of knowledge if you want to learn WebGL basics. The code is stripped out of all comments and console logs (with only some minor whitespaces and empty lines remaining for readability) and it’s already around 80 LOC long! What’s worse, it only draws a single, pink triangle on your screen - nothing fancy! Also, the code for HTML, CSS and GLSL shaders is not included. Well, that’s not so good, but before we dive into complaining about how bad this whole situation is and what are the best ways to solve it, let’s take a quick look at what exactly the above code is doing, shall we?
In the first helper function createShader, we interact with WebGL functional API in order to create/register GLSL shader. Shaders are nothing more than functions written in GLSL (GL Shader Language) - a language similar to C/C++ which allows us to manipulate on structures like vectors, matrices etc. There are 2 of them needed to create WebGL Program instance - Vertex and Fragment shaders. The Vertex one is responsible for computing vertex positions for later rasterization from 3D space to 2D view on user’s screen. Fragment shader, on the other hand, computes colors for given pixels. You first have to write the code for your shaders within their main methods and then properly initialize them through given WebGL method. Writing GLSL can be quite a hassle for ones only familiar to JS. Next, we have createProgram function where we create and link the WebGL program - the connection of both shaders is created. Finally, we come to the main function where everything comes together. First, we initialize WebGL with the given Canvas element. We make use of previously defined helper functions and pass vertex data of the triangle we want to draw.
It’s just a short description of what’s going on. It’s not meant to be any kind of WebGL tutorial (for this kind of thing, again, see WebGL Fundamentals or other similar sources). I just wanted to give you a glimpse of what writing bare-bones WebGL means. Now that we have this out of the way, let’s explore our solution to this problem - WebGL libraries and tools!
Help’s coming!
Because of the WebGL complexity (and the fact that it’s a really useful tool), many libraries and tools have been created in order to help developers create better visual experiences without any bigger problems. Let’s list’em all - both the big and small ones!
Three.js
Three.js is arguably the most popular solution to WebGL problem. If you’ve ever been even for not that big amount of time into WebGL business, there’s a big chance that you’ve heard or even used this library. At its core, Three.js allows you to manipulate your WebGL scene in for of tree of nodes like camera, cube, light etc. (that’s something that many other competitive pieces of software do too) Three.js main goal is to provide the developer with easier, higher-level access to WebGL, which it’s doing really well. This way Three.js remains an easy-to-use and performant tool. On the side-note, there’s a general opinion of not well-maintained documentation of this project. And, while this might be a bit true, its constantly improving and the big community behind Three.js have you backed-up.
Babylon.js
Babylon.js is advertised as a framework for games built on top of modern web APIs (Web Audio, WebVR, WebGL, etc.) with support for WebGL 2 too. It provides first-class support for TypeScript as it’s the language that it’s written in. Its known for being heavily backed-up by Microsoft. The API is based on ideology similar to the one of Three.js. Being full-fledged game engine it is, it also provides better support for game-programming-related functionality. Babylon.js also features well-written documentation, which is important for such vast API. And, as good as it might be, it comes at a cost. While Three.js weights around 133 KB minified and gzipped, Babylon more than triples that size, without any better support for tree shaking.
Playcanvas
Playcanvas is full-blown WebGL 1 / 2 game engine. While the main part of this whole framework - engine - is open source, Playcanvas is mostly known for its editor and development environment. The editor is free for public projects but (sadly and naturally 😕) paid for private. So, focusing on the engine itself - it has great documentation and many, many features. It’s based on ECS (Entity Component System) architecture, which resolves in fine development experience. The worst thing about this whole engine (at least for me) is the lack of official NPM support, but it’s mostly doing its job nicely.
ClayGL
ClayGL is relatively young and small WebGL library but I think it’s worth mentioning. Mainly because of its modularity and great support for tree shaking, allowing to make it as small as 22 KB when only using its core functionality. Sadly, the documentation and included TypeScript typings are just not good enough.
A-Frame
A-Frame is just the right tool for those who don’t like JavaScript much (how dare you 😅) or just like the more HTMListic approach to different things. That’s mainly due to the fact that that, with A-Frame, you create your scene with custom HTML tags and attributes. Backed-up by Mozilla, A-Frame is a project mainly which the main goal is to provide a great tool for developing WebVR content but it can also be used for standard WebGL. You now might be thinking that using only HTML tags is kinda limiting and that’s true! That’s the exact reason for why A-Frame gives you access to underlying Three.js instance (it’s built on top of it) within its ECS architecture. All this and even more you can find it very much complete A-Frame docs.
PixiJS
All of above libraries have focused mainly on 3D and VR content, but WebGL isn’t limited to that. PixiJS is a great example of how good WebGL can be in 2D space. If you’re into that kind of things, PixiJS is used by many big names in both tech and other industries. Great performance, documentation, examples, TypeScript typings - what more would you want? 😅
Regl
With Regl into the party, we’re diving deep into WebGL ocean. Regl is rather a WebGL helper than a library. It provides you with the right methods for doing repetitive tasks (very common in WebGL) with not-so-much low-level API. If your goal is to get things done in a low-level way, then that’s your way to go.
TWGL.js
TWGL.js might be even lower level alternative than Regl. Here, your given total access and control over the underlying WebGL with only some helper functions provided. Again, if that’s your kind of thing, then it might be worth a look.
The other purposes
As you may already know, WebGL has a vast range of use-cases. From most popular games to complex CAD visualizations. I think one library, which may not be so important to some, is worth a look Deck.gl is a library, developed by Uber, for custom map-based visualizations. With help of other libraries like Mapbox GL JS (used for showing custom-styled maps using vector data sources) and Luma.gl (WebGL 1 / 2 visualizations), it’s able to create some impressive visualizations.
What’s even more interesting is the use of WebGL outside of just visuals and showcases. Did you know, that by utilizing WebGL GPU-based nature you can take advantage of GPGPU parallelism and performance? A great example of such use-case is a library called Gpu.js which does just that. You can try it and see for yourself how fast and powerful GPGPU computing can be when it comes to big numbers.
It doesn’t end here…
There’s a lot of amazing WebGL tools and libraries that are great. The ones listed above are just my favorites and only well-maintained ones (at least at the time of writing 😀), so you can just use one of them without worrying about maintainability. That’s all.
If you liked the article, consider sharing it with buttons below. Also, you can follow me for more up-to-date content (if you want 🦄)
If you need
Custom Web App
I can help you get your next project, from idea to reality.