From Nothing to Something in WebGL using regl

I’ve been telling all my friends lately how great and easy regl is, but the truth is it’s only easy if you already know the ins and outs of setting up a modern development environment in the first place. Which really sounds like way more than it is. But it’s taken me a couple years to figure out a lot of this stuff, and I’m still not there. So here’s a quick walkthrough on setting up regl in a nice and simple and modern JavaScript environment. It’s a setup. It’s not the perfect setup, but it’s super simple and it’ll get you off the ground. I use it all the time. Enough talk. Let’s go.

Initialize it

First we’ll make a project. Let’s start from the beginning. Yarn is great for managing JS dependencies. npm too. Don’t bikeshed. So let’s use npm at the moment. You can create a new project from the command line with:

1
2
3
$ mkdir cool-project
$ cd cool-project
$ npm init -y

That made an empty directory with a package.json in it. Let’s create a file called index.js and start with the JS developer’s favorite sanity-check:

1
alert('hello, world!');

Technically we have a functioning project, but there’s no way to see it just yet. To use it, we’ll have to add a couple scripts. Let’s use the good parts of ES6 and fire it up with a development server. budo is awesome for this. budo lets you run javascript files in the browser without having to write any html. To install budo and some other great tools we’ll want, run:

1
$ npm install -D browserify es2040 budo indexhtmlify

I added indexhtmlify since we’ll want that one in a just a minute. Instead of typing out long commands on the command line, let’s add a development server script and a build script to package.json:

1
2
3
4
5
6
7
8
9
10
{
  "name": "cool-project",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "start": "budo index.js --open --live --host localhost -- -t es2040",
    "build": "browserify index.js -t es2040 | indexhtmlify > index.html"
  },
  ...
}

The first command will start a live-reloading development server. Additionally it applies the es2040 browserify transform so that we can use some more modern features of JS without sacrificing compatibility with old browsers. The second script just does the same thing, except it uses indexhtmlify to wrap the final JS in some minimal html. I really wish I’d known about such a simple process for this earlier on.

Run it

That’s it. We’ve got a project. Let’s run it:

1
$ npm run start
Hello!
Hello!

To make something interesting, I’m going to use the regl (at times pronounced “re-gal”) library because I love it and because it’s so easy. To install and save it as a dependency:

1
$ npm install -S regl regl-camera bunny angle-normals

I added also added a couple more dependencies we’ll want. Let’s add the content below. There’s a bit going on here. To summarize, you’re writing a small program (the vertex shader) to tell the gpu how triangles in three dimensional space get mapped to coordinates on your screen, followed by another program (the fragment shader) that tells it what color each pixel is. The goal here isn’t to explain all the details. The goal here is just to get you a good setup so that you can play with things and discover for yourself. Try out some of the examples.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
const regl = require('regl')();
const bunny = require('bunny');
const angleNormals = require('angle-normals');
const camera = require('regl-camera')(regl, {
  distance: 30,
  phi: 0.7,
  theta: 1.5,
  center: [0, 5, 0]
});

const drawBunny = regl({
  vert: `
    precision mediump float;
    attribute vec3 position, normal;
    uniform mat4 projection, view;
    varying vec3 color;
    void main () {
      color = normal;
      gl_Position = projection * view * vec4(position, 1);
    }
  `,
  frag: `
    precision mediump float;
    varying vec3 color;
    void main () {
      gl_FragColor = vec4(color, 1);
    }
  `,
  attributes: {
    position: bunny.positions,
    normal: angleNormals(bunny.cells, bunny.positions)
  },
  elements: bunny.cells
});

regl.frame(() => {
  regl.clear({color: [0, 0, 0, 1]});
  camera(() => {
    drawBunny();
  });
});

Deploy it

We’ve got a project! Up to now we’ve been using a development server and testing it locally. If you want to stick the result on, say, Github (because for any faults, it’s pretty great), you can just run

1
$ npm run build

The result is a file called index.html in the main directory. Push that to github, turn on the web server option in the repo settings, and you’ve got yourself a live webpage.

Hopefully you’re looking at a bunny. Let me know if you run into any trouble!

interactive-frame[width=100% height=300 border=#eee](http://rickyreusser.com/demos/hello-regl/)

Sorry about the mouse wheel behavior.

Summarizing it

To summarize again the tools we just used:

  • npm: installs javascript modules from npmjs.com
  • browserify: turns multiple files with require(...) statements – which the browser knows nothing about – into a single file the browser can run.
  • budo: test out javascript in the browser without writing any html
  • es2040: lots of new features have made there way into JavaScript lately. This plugin picks the good features and compiles your fancy es2040 down to code that will run in old browsers too.
  • indexhtmlify: a super simple utility that wraps your javascript in some boilerplate html so you can throw a web page onto the internet and still not have to write any html.
  • regl: Functional WebGL

🚀