VSCode and WebGL Development

I start learning about WebGL recently. The editor I use is VSCode which is an awesome editor for frontend development, but still, I found some issues and I think I should take some notes.

Most tutorials I have seen start like this: first create index.html and main.js file.

Put a <canvas> element in index.html

<canvas id="canvas"></canvas>

In main.js, grab the canvas element to use it as a WebGL rendering context

const canvas = document.getElementById('canvas'); 
// or const canvas = document.querySelector('#canvas');
const gl = canvas.getContext('webgl');

Then use gl object for basically almost everything.

// methods
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.attachShader();
gl.vertexAttrib1f();
gl.getAttribLocation();

// constants
gl.COLOR_BUFFER_BIT;
gl.VERTEX_SHADER;
gl.COMPILE_STATUS;

The problem I had was that VSCode was unable to display code IntelliSense for gl object because VSCode found it has the type of any — or VSCode didn’t know what type of the object actually is, thus I missed its awesome IntelliSense, which is unfortunate.

with type any, vscode does not know much about it

First I thought it would be fine. Maybe I can live with this.

In fact, the gl object has a type of WebGLRenderingContext. And if we check the specs of this interface, there are over 200 properties (constants) and methods in this object type.

There is 0% chance that I could remember them all, and there are more than 87.525% chance that I will make a typo in the code when typing those property and method names manually by hand.

How to get help from VSCode? 🤔

The actual problem is that VSCode cannot guess the type of the returned canvas element from document.getElementById() or document.querySelector() methods.

By using document.getElementById(), HTMLElement type is returned.

By using document.querySelector(), Element type is returned.

Neither of them is enough. We will need to be more specific that the type we need for the canvas element is HTMLCanvasElement .

I could think of 2 workarounds.

Option 1: use document.createElement()

Instead of creating a <canvas> element in the HTML, we can also create it in JavaScript code and then put it in the page dynamically.

const canvas = document.createElement('canvas');
document.querySelector('body').appendChild(canvas);
const gl = canvas.getContext('webgl');

Creating a canvas with document.createElement('canvas'), VSCode knows that it has type of HTMLCanvasElement which gives a better sense with calling .getContext() on the canvas.

Passing in a string webgl as a parameter, VSCode knows the return type of the method that should be WebGLRenderingContext type.

the built-in `lib.dom.d.ts` type definitions file in VSCode

Now we can get code autocompletion our gl object working.

Option 2: Use TypeScript’s Type Assertion

If the project is using TypeScript, we can use Type Assertion to tell VSCode about the canvas object type.

const canvas = <HTMLCanvasElement>document.getElementById('canvas');

This tells the TypeScript compiler to treat canvas object as HTMLCanvasElement type, instead of the default HTMLElement type. It is not the same with type casting though.

When VSCode knows the type, it shows the code autocompletion like so.

Oops, there’s another typo 😅

Code Editor as a Learning Resource

Having IntelliSense in VSCode is really helpful. Not only it helps me to code faster with less mistakes, but I also see it as a tool to help learning new methods/APIs of WebGL without leaving in the editor.

For example, hovering over a method name gives me the method arguments, how they order, and what the returns. Otherwise I would have to switch to the browser and google every time to see what the 5th argument is.

Pressing Cmd + click (or Ctrl + click on Windows I believe) on the a method or a property name will jump to the definition. If I’m lucky, there will be documentation in the definition file. It’s not quite there yet for WebGL APIs but I think it will eventually come built-in like other APIs.


Bonus: Setting up webpack and TypeScript in My Learning Project

I started the learning project with vanilla JavaScript because I wanted to avoid setting up build tools if I don’t need them. However, with TypeScript, this is unavoidable. At least we have to set up some build steps to transpile TypeScript into JavaScript.

Here is how I set up webpack to use TypeScript in the project (with my very limited knowledge about webpack itself).

1. Install TypeScript

$ npm install --save-dev typescript

2. Install webpack

$ npm install --save-dev webpack webpack-cli webpack-dev-server

3. Install TypeScript loader with sourcemaps support

$ npm install --save-dev awesome-typescript-loader source-map-loader

(or install everything above in a single command).

4. Create webpack.config.js configuration file for webpack.

const path = require('path');
module.exports = {
mode: 'development',
entry: './src/index.ts',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
devServer: {
contentBase: './dist'
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx']
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'awesome-typescript-loader'
}
]
}
};

5. Start the webpack dev server

$ npx webpack-dev-server

And now I can use TypeScript in my learning project.