WebGL and Three.js — 3D Graphics in the Browser
In this tutorial, you'll learn about WebGL and Three.js. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
WebGL is a JavaScript API for rendering 2D and 3D graphics in the browser without plugins, and Three.js is a high-level library that makes WebGL accessible by abstracting shaders, buffers, and pipeline management.
What You'll Learn & Why It Matters
In this tutorial, you will learn how to create 3D scenes in the browser using WebGL directly and through the Three.js library. You will build interactive 3D visualizations that run on any device with a web browser.
Real-world use: Product configurators, data dashboards, architectural walkthroughs, and even antivirus UI dashboards use WebGL. At DodaTech, we use Three.js for real-time security analytics visualizations.
Prerequisites
- OpenGL Basics (previous)
- JavaScript fundamentals
- Basic understanding of 3D math
Learning Path
flowchart LR A[OpenGL Basics] --> B[WebGL and Three.js] B --> C[Shader Programming] B --> D[Texture Mapping] B --> E[Post-Processing] C --> F[Compute Shaders] B:::current classDef current fill:#f90,color:#fff,stroke:#333,stroke-width:2px
WebGL vs OpenGL
WebGL 2.0 is based on OpenGL ES 3.0. The concepts are identical: you have a Rendering Pipeline, shaders written in GLSL, vertex buffers, and framebuffers. The main difference is the host language (JavaScript) and the absence of a native windowing system.
<canvas id="glCanvas" width="800" height="600"></canvas>
<script>
const canvas = document.getElementById('glCanvas');
const gl = canvas.getContext('webgl2');
if (!gl)
{
console.error('WebGL 2.0 not supported');
}
gl.clearColor(0.1, 0.1, 0.2, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
</script>
Three.js Fundamentals
Three.js wraps raw WebGL and provides a clean object-oriented API. The three core objects are the scene, camera, and renderer.
import * as THREE from 'three';
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, 800 / 600, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(800, 600);
document.body.appendChild(renderer.domElement);
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshPhongMaterial({ color: 0x44aa88 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(5, 5, 5);
scene.add(light);
camera.position.z = 5;
The Animation Loop
function animate()
{
requestAnimationFrame(animate);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
Expected output: A rotating turquoise cube with directional lighting on a dark background.
flowchart TD A[Scene] --> B[Mesh] B --> C[Geometry + Material] A --> D[Light] A --> E[Camera] F[Renderer] --> G[Render Scene with Camera] G --> H[Animate Frame] H --> F
Working with Geometry
Three.js provides geometry primitives and supports custom geometry:
// Custom triangle geometry
const geometry = new THREE.BufferGeometry();
const vertices = new Float32Array([
-1.0, -1.0, 0.0,
1.0, -1.0, 0.0,
0.0, 1.0, 0.0
]);
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
const material = new THREE.MeshBasicMaterial({ color: 0xff6600, side: THREE.DoubleSide });
const triangle = new THREE.Mesh(geometry, material);
scene.add(triangle);
Textures and Materials
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load('brick.jpg');
const material = new THREE.MeshStandardMaterial({
map: texture,
roughness: 0.7,
metalness: 0.2
});
MeshStandardMaterial uses physically-based rendering (PBR), which responds realistically to lights. Properties like roughness and metalness control how light interacts with the surface.
OrbitControls for Interaction
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
function animate()
{
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
Writing Raw WebGL Shaders
When you need custom effects, you can write GLSL shaders with Three.js:
const vertexShader = `
varying vec2 vUv;
void main()
{
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
const fragmentShader = `
varying vec2 vUv;
uniform float time;
void main()
{
vec3 color = vec3(
sin(vUv.x * 10.0 + time) * 0.5 + 0.5,
cos(vUv.y * 10.0 + time) * 0.5 + 0.5,
sin((vUv.x + vUv.y) * 10.0 + time) * 0.5 + 0.5
);
gl_FragColor = vec4(color, 1.0);
}
`;
const material = new THREE.ShaderMaterial({
vertexShader: vertexShader,
fragmentShader: fragmentShader,
uniforms: { time: { value: 0 } }
});
Performance Considerations
- Use
BufferGeometryinstead ofGeometry(deprecated) - Reuse materials and geometries across meshes
- Use
mergeBufferGeometriesfor static objects - Limit draw calls by combining meshes
- Use LOD (level of detail) for distant objects
Common Errors & Mistakes
1. CORS Errors with Textures
Mistake: Loading textures from a file:// URL or a different domain without CORS headers.
Fix: Serve your page via HTTP (localhost or a web server) and ensure textures come from the same origin or have CORS permissions.
2. Not Calling renderer.setSize
Mistake: Using the default renderer size (300x150) and wondering why the output is small.
Fix: Always call renderer.setSize(width, height) matching your canvas or viewport dimensions.
3. Forgetting requestAnimationFrame
Mistake: Calling render() once in a loop without requestAnimationFrame, causing the browser tab to freeze.
Fix: Use requestAnimationFrame for smooth, efficient animation that pauses when the tab is hidden.
4. Not Setting Camera Aspect Ratio
Mistake: Creating a perspective camera with the default aspect ratio that looks stretched.
Fix: Match the camera aspect ratio to the renderer size: new THREE.PerspectiveCamera(75, width / height, 0.1, 1000).
Practice Questions
Question 1
What is the difference between WebGL and Three.js?
Show answer
WebGL is a low-level JavaScript API for GPU rendering. Three.js is a high-level library that wraps WebGL, handling buffers, shaders, and pipeline management so you can create 3D scenes with minimal code.Question 2
Why use OrbitControls in a 3D scene?
Show answer
OrbitControls provides mouse-driven camera manipulation (rotate, pan, zoom) which is essential for interactive 3D visualizations. It handles all the math for orbiting around a target point.Question 3
What is the purpose of the animation loop?
Show answer
The animation loop using `requestAnimationFrame` updates scene state and re-renders each frame, creating smooth animation. It pauses when the tab is hidden, saving battery and CPU.Question 4
How does MeshStandardMaterial differ from MeshBasicMaterial?
Show answer
MeshBasicMaterial renders without lighting (flat color/texture). MeshStandardMaterial uses PBR lighting calculations, responding to scene lights with proper reflections, shadows, and material properties.Challenge
Build a 3D solar system visualization using Three.js with orbital animation, textured planets, a starfield background, and mouse-controlled camera. Use at least four planets with different orbital speeds and sizes.
FAQ
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Author: DodaTech | Last updated: June 21, 2026
DodaTech tutorials are built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro — security tools used by millions worldwide.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro