/webgl-101

learning webgl on egghead.io

Primary LanguageTypeScript

VSCode and WebGL Development

Read this on Medium. The code in this repo is based on the course Create 3D Graphics in JavaScript using WebGL on Egghead.io.


I start learning about WebGL recently from WebGL Tutorials on the internet. The editor I use is VSCode which an is awesome editor for frontend development, but still I found some issues and I think I should take a note.

Most tutorials I have seen start like this

  1. Create a <canvas> element in index.html
<canvas id="canvas"></canvas>
  1. In main.js, use the canvas element as a WebGL rendering context
const canvas = document.getElementById('canvas'); // or 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 finds it has the type of any - VSCode does not know what type of the object is, thus it cannot help with 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 get along with this.

In fact, the gl object has 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 returned canvas element.

By using document.getElementById(), we got HTMLElement type returned.

By using document.querySelector(), we got Element type returned.

I could think of 2 workarounds.

Option 1: use document.createElement()

Instead of pre-create <canvas> element in the HTML, we can alo create it in JavaScript code and put it in the page via appendChild().

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 it.

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

(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 IntelliSense like so.


Setting up 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. With TypeScript, this is unavoidable. At least we have to set up some build steps to transpile TypeScript to JavaScript.

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

  1. Install TypeScript
$ npm install --save-dev typescript
  1. Install webpack
$ npm install --save-dev webpack webpack-dev-server
  1. Install TypeScript loader with sourcemaps support
$ npm install --save-dev awesome-typescript-loader source-map-loader

or you can install everything above in a single command.

  1. 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']
	},
	devtool: 'source-map',
	module: {
		rules: [
			{
				test: /\.tsx?$/,
				loader: 'awesome-typescript-loader'
			}
		]
	}
};
  1. Start the webpack dev server
$ npx webpack-dev-server

And now I can use TypeScript in my learning project.