The Bridge to the GPU
For decades, high-end 3D graphics were confined to native applications like AAA games or CAD software. The web was flat (and clunky and boring!). When WebGL (Web Graphics Library) arrived, it unlocked the power of those monster GPUs to be used inside the browser, but it came with a steep learning curve.
Just for completeness, this is what that looks like.
Writing raw WebGL is notoriously verbose. To draw a single 2d box in the browser, we need to first create a canvas element:
<canvas id='glCanvas' width='640' height='480'></canvas>
<script id='vs' type='x-shader/x-vertex'>
attribute vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
</script>
<script id='fs' type='x-shader/x-fragment'>
void main() {
gl_FragColor = vec4(1.0, 0.5, 0.0, 1.0); // Orange
}
</script>And then we have to fill it with our 2d box as generated by the GPU by rendering two triangles:
const canvas = document.querySelector('#glCanvas');
const gl = canvas.getContext('webgl');
if (!gl) {
alert('WebGL not supported');
}
// 1. Compile Shaders
function createShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
return shader;
}
const vsSource = document.getElementById('vs').text;
const fsSource = document.getElementById('fs').text;
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vsSource);
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fsSource);
// 2. Link Program
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
// 3. Define Geometry (Square made of two triangles)
// Coordinates are Clip Space: -1.0 to +1.0
const vertices = new Float32Array([
-0.5, 0.5, // Top Left
0.5, 0.5, // Top Right
-0.5, -0.5, // Bottom Left
0.5, -0.5 // Bottom Right
]);
// 4. Create Buffer and Upload Data
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// 5. Tell GPU how to read the buffer
const positionLocation = gl.getAttribLocation(program, 'position');
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
// 6. Draw
gl.clearColor(0, 0, 0, 1); // Black background
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);And this is the result — an orange square rendered by the GPU:
What a headache.
Three.js makes all of this much easier by providing a high-level, object-oriented API that allows developers to think in terms of scenes, lights, and materials rather than points, lines, and triangles.
With Three.js, the same effect (an orange box, centered in a canvas) can be achieved in less lines of code and a much more human-understandable code structure.
import * as THREE from 'three';
// 1. Setup Scene & Camera
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
// 2. Setup Renderer
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 3. Create the Box (Geometry + Material = Mesh)
const geometry = new THREE.BoxGeometry(2, 2, 2);
const material = new THREE.MeshBasicMaterial({ color: 0xffa500 }); // Orange
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// 4. Animation Loop
function animate() {
requestAnimationFrame(animate);
// Let's make it spin! (Something very hard in raw WebGL)
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();I think that is worth learning. I bet that you do, too.