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.
Many 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.
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 programmatically.
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.
lib.dom.d.ts
` type definitions file in VSCodeNow we can get code auto-completion 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 auto-completion like so.
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.