A comparison of various libs for drawing things with WebGL.
As I learn the fundamentals of WebGL, I am experimenting various techniques for rendering pictures or animations with low-level APIs and shaders (SDF, raymarching, particles...).
I don't need the features of the big WebGL libraries and I like to keep my projects lightweight, so I searched for a sweet spot between ease of use (abstraction of the WebGL boilerplate, modern syntax...), efficiency (light weight, tree shaking...), and support for all environments (SSR, workers...).
- canvas positionned and sized in CSS (not created by the lib)
- support for WebGL 2 (GLSL version 3.30)
- types
- works on the server (SSR/SSG) and on a web worker (OffscreenCanvas)
- control over vertex and fragment shaders
- ability to set uniforms and update their values over time
- Native WebGL: the most complex option but also the lightest.
- TWGL : a lightweight library that provides abstractions over the native WebGL API.
- four: sits nicely in the spot where you need some basic functionalities like perspective, multiple objects or load external files, but don't need the full power of three.js, while being extremely lightweight.
- shree: roughly identical to four but the repo seems dead.
- glslCanvas: probably the best compromise between ease of use, weight and functionality for basic use cases. You can throw a CDN script on an HTML document, provide your shaders and you are good to go without writing any JavaScript code.
- ogl: slightly lower level than four but offers features like orbit controls and built-in meshes
- regl: seems a bit outdated because it's too heavy for simple use cases and for more complex ones three.js is probably a better choice.
- three: by far the most popular and the default choice for any 3D project that needs more than basic functionalities (if the other solutions above are not enough).
- pixi: claims to be the fastest 2D WebGL renderer. I guess it should be reserved for advanced 2D use cases, because it's even heavier than three.
Library | Canvas styled in CSS | WebGL 2 | Types | Works on server / worker | Complexity |
---|---|---|---|---|---|
native WebGL | ✅ | ✅ | ✅ | ✅ | **** |
TWGL | ✅ | 🟧 | ✅ | ✅ | *** |
four | ✅ | ✅ | ✅ | 🟧 | *** |
shree | ❌ | ❌ | ❌ | ❌ | *** |
glslCanvas | ✅ | ❌ | ❌ | ❌ | * |
ogl | ✅ | ✅ | ✅ | ✅ | *** |
regl | ✅ | ❌ | ✅ | ✅ | ** |
three | ✅ | ✅ | ✅ | ✅ | *** |
pixi | ✅ | ✅ | ✅ | ✅ | *** |
![](https://private-user-images.githubusercontent.com/22420399/337782330-8221b07f-0398-488e-94b2-0561831daadb.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzk5NDE5MTIsIm5iZiI6MTczOTk0MTYxMiwicGF0aCI6Ii8yMjQyMDM5OS8zMzc3ODIzMzAtODIyMWIwN2YtMDM5OC00ODhlLTk0YjItMDU2MTgzMWRhYWRiLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAyMTklMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMjE5VDA1MDY1MlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTZmNzIwYzczYWMwZjAxZGUzN2E4NDFjOTQ3ZDc5MmI5MzJmNTA0NGYwODlkODc1YTgzNjYzMGE1MzZmMWFjZTUmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.A1AU1oiVMArgWlpwBwJb4IIZwGSkKb7I37QMutc7B9g)
This is one of the simplest things to draw with WebGL, which allows to compare the syntax and weight of the libraries on a bare bones setup.
A render made with a fragment shader using only the UV coordinates given by the vertex shader, and optionally a uTime
uniform to make an animation.
Sorted by weight.
Library | Code | Weight (resource) | Weight (gzip) |
---|---|---|---|
native WebGL | link | 3.2kB | 1.7kB |
four | link | 18.5kB | 7.1kB |
TWGL | link | 20.6kB | 7.2kB |
shree | link | 27.5kB | 8.5kB |
glslCanvas | link | 28.5kB | 9.6kB |
ogl | link | 44.5kB | 13.5kB |
regl | link | 125kB | 42.2kB |
three | link | 452kB | 115kB |
pixi | link | 458kB | 139kB |
- Three and Pixi are way too heavy for this use case, and this is obviously not what they are made for. Regl is also too heavy in my opinion.
- among the off-the-shelf libraries, glslCanvas is by far the easiest to use, but it's surprisingly heavier than four or shree.
- four, shree and ogl are quite similar, ogl being a little lower-level, and four closer to three.js.
- Native WebGL is really light, and with the little helper that I made (
useWebGLCanvas
), the usage is really easy. I think this is the solution that I would pick for this use case.
![](https://private-user-images.githubusercontent.com/22420399/340382784-2cb0aba0-b467-4d71-8559-a28442dfc15f.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzk5NDE5MTIsIm5iZiI6MTczOTk0MTYxMiwicGF0aCI6Ii8yMjQyMDM5OS8zNDAzODI3ODQtMmNiMGFiYTAtYjQ2Ny00ZDcxLTg1NTktYTI4NDQyZGZjMTVmLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAyMTklMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMjE5VDA1MDY1MlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWZkMzkzNTY1ZjA4ODFjYzRjNjBjZjhkZTA3ZTdlMzRiZTRjNDdmOTBlMmE3MjNlMzQxNDUwOGZkNzZkYTc4NTUmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.ZFQygEfALohwVMcd3N2OqxpeZJKkou5-Ldijva3lYjg)
A little animation in a fragment shader using user input.
A render a little more complex made with a fragment shader and using the UV coordinates, a uTime
uniform and another one driven by user input.
It's similar to the previous benchmark, just a little closer to a real world scenario. And the user input is important to test the usage in a web worker, because unlike for the time, a web worker cannot watch user interactions. So there has to be messages between the main and worker scripts.
Sorted by weight.
shree, glslCanvas and regl are excluded from this benchmark and the next ones, because they lack some features that I want (support for WebGL2, usage in web worker...).
Library | Code | Weight (resource) | Weight (gzip) | Web Worker |
---|---|---|---|---|
native WebGL | link | 6kB | 2.7kB | ✅ |
four | link | 21.3kB | 8kB | 🟧 |
ogl | link | 47.3kB | 14.6kB | ✅ |
three | link | 454kB | 116kB | ✅ |
pixi | link | 461kB | 140kB | ✅ |
- all projects gained roughly 1kB, which is the weight of the project-specific code, so nothing surprising here.
- four seems to have an issue in a web worker that breaks synchronous ES imports, forcing to use dynamic
import()
. This is a little inconvenient but it's still possible to make it work. - four and ogl renderers don't accept a canvas of type OffscreenCanvas