Setting up a modern webgl environment - part 2

In the last post we began to setup a modern javascript build system. If you didn’t read it, you can catch up here:

https://brandf.github.io/2016/10/10/modern-webgl-environment-part1/

In this post we’re going to continue and setup babel, eslint, and editorconfig. At that point you’ll have an awesome environment to create the next great web app.

If you were following along in part 1, you can continue where we left off, otherwise clone this repo to catch up:

https://github.com/brandf/brandf-environment-part1.git

The first thing we’re going to do is setup an editor config. This is nice to have in an open source project because you don’t know what editor folks are going to use, and so this is a common way to describe what settings they should use.

Simply create a file named .editorconfig that looks like this:

.editorconfig
1
2
3
4
5
6
7
8
9
10
# http://EditorConfig.org
root = true
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
indent_style = space
indent_size = 2

I’m using Visual Studio Code, which doesn’t support .editorconfig out of the box, so I needed to install an extension. Hit Ctrl+P, and type “ext EditorConfig” to open the extensions. Install “EditorConfig for VS Code”.

Now you can test it out by modifying a file to have a trailing space, or no new line, and see if it trims when you save.

Next let’s install a transpiler called Babel. Babel lets you write modern javascript, and compile it down to older javascript (most likely ES5). Babel also acts as a platform for various other language extensions such as JSX (more on this in a future post).

First we need to get the babel package from npm:

1
> npm install --save-dev babel-core

Now we could kick it off manually, but we already have webpack setup so let’s have webpack run babel on all javascript files as part of the bundling process.

The babel loader module does this for us:

1
> npm install --save-dev babel-loader

Next we need to setup webpack to use the loader. Add this to the webpack config:

webpack.config.js
1
2
3
4
5
6
7
8
9
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loaders: ['babel'],
},
],
},

At this point, babel will run, but it doesn’t do much unless we tell it what transformations we want. To do this, we need a babel config file:

1
> echo '{ "presets": ["latest"], "plugins": ["transform-runtime"] }' > .babelrc

Next install the package for the preset and plugin:

1
2
> npm install babel-preset-latest --save-dev
> npm install babel-plugin-transform-runtime --save-dev

At the time of this writing, the ‘latest’ preset brings in es2015, es2016, and es2017 language features. The transform plugin is needed to make generators/async work. Now it’s time to write some modern javascript!

Replace your app.js with this:

src/app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Foo {
static async doIt() {
for (let i = 0; i < 10; i++) {
await Foo.delay(1);
document.body.appendChild(document.createTextNode(i));
}
}
static delay(seconds) {
return new Promise((res) => {
setTimeout(() => {
res();
}, seconds * 1000);
});
}
}
Foo.doIt();

The above is just some toy code to exercise javascript classes (from es2015) and async methods (from es2017).

To see it in action, just launch the server

1
> npm run start

You can play around with it while the server is running and it will reload every time you save.

Now that we’re writing javascript, we’ll soon get annoyed by simple typos causing bugs that aren’t discovered until runtime. YAY dynamic languages. One way to combat this is with static analysis. Since we already have a build step for bundling, we would like to find out about common mistakes at build time instead of runtime.

eslint is great for this. Not only does it find mistakes like typos, but it also keeps the code clean/consistent by enforcing coding standards such as brace style, naming conventions, etc.

1
> npm install eslint-loader --save-dev

And then we need to add it to our loaders list. Make sure it’s the right-most loader so it runs first.

webpack.config.js
1
2
3
4
5
6
7
8
9
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loaders: ['babel', 'eslint'],
},
],
},

Similar to how we configured babel, we’re going to configure eslint with .eslintrc. You can use different rules to suit your taste, but the airbnb set are a good starting point.

1
npm install eslint-config-airbnb --save-dev

.eslintrc
1
2
3
4
5
6
7
8
9
10
{
"extends": "airbnb",
"rules": {
"no-plusplus": "off",
"no-param-reassign": ["error", { "props": false }]
},
"env": {
"browser": true
}
}

Note that I customized a few rules on top of airbnb.

Now when we build, we will get warnings/errors if our code violates the rules. At that point you have two options 1) fix the issues 2) override the rule. Sometimes it makes sense to override it because you know what you’re doing and the rule is to prevent something accedental like using | instead of ||. One way to override it is to do something like this:

1
2
// eslint-disable-next-line no-bitwise
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

Similar to editorconfig, you may want to setup your editor to run eslint. To do that in visual studio code, you type CTRL+P, then “ext ESLint”, and install it. You may also need to install the package globally

1
> npm install eslint -g

You may need to close/reopen VS Code, and now you’ll get red squiggles every time you violate an eslint rule. Try adding a space between a function and it’s opening param…bad developer!

That’s all for part 2. We now have a pretty sophisticated build environment that lets us use the latest language features while still being able to target common browsers.

You can find the end result here: https://github.com/brandf/brandf-environment-part2.git

In part 3 we’ll add some packages to help use WebGL and some babel loaders that help us with CSS and GLSL.

Setting up a modern webgl environment - part 1

With modern browsers, node.js, and github, the javascript ecosystem has grown tremendously. While the language is evolving, user adoption is lagging behind, so developers must make a choice: limit yourself to commonly adopted features (such as ES5), or use a build system to take advantage of modern features and ‘compile’ it down to ES5.

In this post, I’m going to describe how I setup the latter, specifically for making a website that is using WebGL. If you’ve been paying attention over the last few years, you will have noticed that there are plenty of options for javascript build system tools, and it can be confusing what to use and how to set it up so I’m going to focus on what I believe is the simplest system that gives you the most bang for your buck.

Specifically, in this and subsequent posts we’re going build an environment that uses the following tools:

  • git (for source control)
  • node/npm (for package management)
  • webpack (for bundling)
  • babel (for es2017 transpiling)
  • eslint (for static analysis)
  • editorconfig (for editor settings)

Instead of just giving you a yeoman generator that drops a complicated build system in your lap, we’re going to build it up piece by piece from scratch.

First, we need to decide how we’re going to execute the build process. For this, I think the choice is easy - Node.js. Your team is presumably already familiar with javascript, so writing the build system in javascript is a natural choice. Node also ships with a fantastic package manager (npm), so this will make it easy to pull in 3rd party dependencies like webpack and babel.

If you don’t have it already, go ahead and install Node.js from https://nodejs.org/

We’re going to be using github for source control, if you don’t have it, you can get the client from https://desktop.github.com/

Now create a new github repro. I typically do this from here https://github.com/new.

After creating the repo, you’ll get a link that looks like this: https://github.com/brandf/brandf-environment-part1.git, only with your username/repro instead of mine.

Now open up a terminal and we’ll setup a node package for our project.

1
2
3
> git clone <link_to_repo>
> cd <repo_name>
> npm init

Answer the questions, and you’ll have yourself a package.json file. This is a manifest file that npm uses to track your projects dependencies, as well as other metadata in case you decide to publish this module to npm.

At this point there are several directions we could go - Grunt, Gulp, Browserify, etc are commonly used, but for this project I think Webpack is a great choice. Webpack will do a few things for us

1) it understands modules, and even node
packages, so you can easily write modular javascript and Webpack will bundle it all together into something that is easy
to consume in a browser.
2) It coordinates the execution of various other tools, which can transform your code. We will
use this to write modern javascript and have it transpiled (via babel) down to ES5.

To setup webpack, we first need to install it via npm:

1
> npm install webpack --save-dev

Then we’re going to put the webpack configuration in a file called webpack.config.js:

webpack.config.js
1
2
3
4
5
6
7
8
9
10
11
module.exports = {
entry: './src/app.js',
output: {
path: './bin/',
publicPath: '/bin/',
filename: 'bundle.js',
sourceMapFilename: '[file].map',
devtoolModuleFilenameTemplate: 'webpack:///[resource-path]?[loaders]',
},
devtool: 'source-map',
};

This tells webpack that src/app.js is our entry point, and it will traverse all the module dependencies and bundle them up into bin/bundle.js. It will also produce source maps for bundle.js that let you debug the original source files rather than the generated bundle.js file.

For now we’ll just make a placeholder entry point:

1
2
3
> mkdir ./src
> mkdir ./bin
> echo "document.write('hello world');" > ./src/app.js

We can now build the bundle, to produce bin/bundle.js

1
> ./node_modules/.bin/webpack

We run it out of the node modules so that we don’t have any global dependencies except node.js.

To test this, we need to host the bundle in an html file:

index.html
1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<canvas id="gl"></canvas>
<script src="bin/bundle.js" charset="utf-8"></script>
</body>
</html>

Note that I put this in the root of the project to make it easy to use with github pages. The canvas tag is what we will use later to render WebGL.

At this point you can open index.html in a browser and see that the javascript is running. Better yet, we can locally host this as a website using the webpack dev server. This can give you hot module swapping, which makes iterating really fast.

1
> npm install webpack-dev-server -save-dev

To run with the dev server, try this:

1
> ./node_modules/.bin/webpack-dev-server --inline --hot --progress --colors --open

This should open your browser for you, and if you make changes, the browser will automatically reload after you save so you can see the result instantly.

src/app.js
1
document.write('hot swap');

Next let’s move the webpack commands into package.json. Replace the existing script tag with this:

package.json
1
2
3
4
"scripts": {
"build": "./node_modules/.bin/webpack --progress --colors",
"start": "./node_modules/.bin/webpack-dev-server --inline --hot --progress --colors --open"
},

This is a nice thing to do in case you publish this on npm. Folks can just install your package, and build it without knowledge of the specific build tools you used. It’s also easier to execute than digging into node_modules and remembering all the flags.

1
> npm run build

Or to launch the dev server:

1
> npm run start

Finally, let’s check what we have into git. We don’t want to check in the node_modules though, so add a .gitignore file:

1
> echo "node_modules" > .gitignore

And now, commit it to your repo. We even commit ./bin/* because we want the project to work with github pages (more on this in another post).

1
2
3
> git add -A
> git commit -m "setup webpack and npm package"
> git push

That’s all for part 1. You can find the end result here: https://github.com/brandf/brandf-environment-part1.git

In part 2 we’ll hook up babel, eslint, and start writing some modern javascript code.

Hello World

Welcome to my new blog!

A bit about me…I’m a software developer, and I’m interested in platforms and game development. I’ve worked on platforms at Microsoft and HBO, and soon I’ll be joining Facebook to help bring VR to the web.

I’m not sure how this blog will evolve, but the plan is to post about web development, WebGL, WebVR, Chromium, electronics, racing drones, and whatever other topics interest me.

That’s it for now, just wanted to say hi. Stay tuned!