Mastering 3D Web Games: Three.js, glTF, and Cannon.js Essentials
This guide walks you through the core concepts of Three.js, the glTF model format, physics integration with Cannon.js, interaction handling, performance tuning, and a suite of debugging tools, providing practical code snippets and best‑practice tips for building efficient 3D web games.
Introduction
When developing H5 games under tight schedules, many teams stick to 2D visuals, but a 3D physics‑based game can bring fresh experiences. This article shares practical knowledge about Three.js, Cannon.js, glTF models, related tools, and performance considerations to help you launch a 3D project quickly.
Three.js Basics
Scene
A THREE.Scene acts as a container for all objects, lights, and cameras that will be rendered.
Axes
Three.js uses a right‑handed coordinate system: X (thumb), Y (index finger), Z (middle finger).
Camera
Various camera types exist; the most common is PerspectiveCamera. Other types include ArrayCamera, CubeCamera, StereoCamera, and OrthographicCamera for specific use‑cases such as VR or 2.5D views.
Mesh
A mesh combines Geometry (vertices, faces) and Material (surface appearance). Geometry defines the shape, while Material determines color, reflectivity, transparency, etc.
Light
Lights illuminate the scene. Common types are AmbientLight, DirectionalLight, HemisphereLight, PointLight, and SpotLight. Only DirectionalLight, PointLight, and SpotLight can cast shadows.
Shadow
To enable shadows you must set castShadow and receiveShadow on every mesh that participates. The following snippet traverses all child meshes to activate shadows:
object.traverse(function (child) {
if (child instanceof THREE.Mesh) {
child.castShadow = true;
child.receiveShadow = true;
}
});glTF Model Format
Overview
glTF, created by the Khronos Group, minimizes file size and speeds up transmission, parsing, and rendering. Version 2.0 (released 2017) is the current standard.
File Composition
.gltf – JSON describing scene hierarchy, cameras, meshes, materials, animations.
.bin – Binary buffer containing geometry, animation data, directly uploadable to GPU.
.jpg/.png – Texture images.
Exporting
Export plugins exist for most 3D tools (3DS Max, Maya, Blender). When a direct exporter is unavailable (e.g., Cinema 4D), export to an intermediate format, import into Blender, then export glTF.
Loading in Three.js
var gltfLoader = new THREE.GLTFLoader();
gltfLoader.load('./assets/box.gltf', function (scene) {
// model object
scene.add(scene);
});Animation
glTF can embed animation clips. After loading, the animations array contains AnimationClip objects that can be played via THREE.AnimationMixer:
let gltfLoader = new THREE.GLTFLoader();
let mixer = null;
gltfLoader.load('./assets/box.gltf', function (scene) {
let object = scene.scene;
let animations = scene.animations;
if (animations && animations.length) {
mixer = new THREE.AnimationMixer(object);
for (let i = 0; i < animations.length; i++) {
mixer.clipAction(animations[i]).play();
}
}
scene.add(object);
});
function update() {
let delta = clock.getDelta();
mixer.update(delta);
}Draco Compression
Draco reduces geometry size by converting glTF to .glb with compressed .bin. Use gltf-pipeline to compress:
$ npm install -g gltf-pipeline
$ gltf-pipeline -i model.gltf -o model.glbWhen loading compressed files, include the Draco decoder libraries:
let loader = new THREE.GLTFLoader();
THREE.DRACOLoader.setDecoderPath('/examples/js/libs/draco');
loader.setDRACOLoader(new THREE.DRACOLoader());
loader.load('models/gltf/box.gltf', function (gltf) {
scene.add(gltf.scene);
});Cannon.js Physics Engine
Why Cannon.js
Among several JavaScript physics libraries (Cannon.js, Oimo.js, Ammo.js, Energy.js), Cannon.js offers a full‑featured, actively maintained solution with collision detection, friction, restitution, and constraints.
Setup Steps
Initialize World
let world = new CANNON.World();
world.gravity.set(0, -10, 0);
world.broadphase = new CANNON.NaiveBroadphase();Create Dynamic Sphere
let sphereShape = new CANNON.Sphere(1);
let sphereBody = new CANNON.Body({ mass: 5, position: new CANNON.Vec3(0, 10, 0), shape: sphereShape });
world.add(sphereBody);Create Static Plane
let groundShape = new CANNON.Plane();
let groundBody = new CANNON.Body({ mass: 0, shape: groundShape });
groundBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2);
world.add(groundBody);Visual Meshes (Three.js)
// Plane mesh
let groundGeometry = new THREE.PlaneGeometry(20, 20, 32);
let groundMaterial = new THREE.MeshStandardMaterial({ color: 0x7f7f7f, side: THREE.DoubleSide });
let ground = new THREE.Mesh(groundGeometry, groundMaterial);
scene.add(ground);
// Sphere mesh
let sphereGeometry = new THREE.SphereGeometry(1, 32, 32);
let sphereMaterial = new THREE.MeshStandardMaterial({ color: 0xffff00 });
let sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
scene.add(sphere);Simulation Loop
function update() {
requestAnimationFrame(update);
world.step(1 / 60);
sphere.position.copy(sphereBody.position);
sphere.quaternion.copy(sphereBody.quaternion);
}Advanced Topics
Custom Material Contact
let ground_cm = new CANNON.Material();
let sphere_cm = new CANNON.Material();
let contact = new CANNON.ContactMaterial(ground_cm, sphere_cm, { friction: 1, restitution: 0.4 });
world.addContactMaterial(contact);Disable Physical Response boxBody.collisionResponse = false; Scale a Body
boxBody.updateMassProperties();
new TimelineMax().to(sphereBody.shapes[0], 2, { radius: 0.2 });Interaction with Raycaster
Use THREE.Raycaster to convert 2D screen coordinates into a 3D ray and detect intersected objects.
let raycaster = new THREE.Raycaster();
let mouse = new THREE.Vector2();
function onTouchEnd(ev) {
var event = ev.changedTouches[0];
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
let intersects = raycaster.intersectObjects(scene, true);
for (let i = 0; i < intersects.length; i++) {
console.log(intersects[i]);
}
}Performance Tips
Model Detail – Prefer low‑poly meshes and use normal/bump maps to simulate complexity.
Lights & Shadows – Use directional or spot lights for realism; limit shadow map resolution and disable shadows on unnecessary meshes.
Antialiasing – Enable renderer.antialias = true only when performance budget allows.
Pixel Ratio – Set a fixed ratio (e.g., renderer.setPixelRatio(2)) instead of the device’s maximum to avoid excessive GPU load.
Useful Development Tools
OrbitControls – Drag to rotate the camera and scroll to zoom.
new THREE.OrbitControls(camera, renderer.domElement);glTF Viewer – Web and desktop viewers for quick model inspection.
Camera Helper
let helper = new THREE.CameraHelper(camera);
scene.add(helper);Light Helper
let lightHelper = new THREE.DirectionalLightHelper(light);
scene.add(lightHelper);AxesHelper
let axesHelper = new THREE.AxesHelper(10);
scene.add(axesHelper);Cannon Debug Renderer
let cannonDebugRenderer = new THREE.CannonDebugRenderer(scene, world);
function render() {
requestAnimationFrame(render);
cannonDebugRenderer.update();
}dat.GUI – Real‑time UI for tweaking parameters.
let gui = new dat.GUI();
gui.add(opts, 'x', -3, 3);
gui.add(opts, 'y', -3, 3);
gui.add(opts, 'scale', 1, 3);Stats.js – Monitor FPS, frame time, and memory usage.
var stats = new Stats();
stats.showPanel(1);
document.body.appendChild(stats.dom);Conclusion
By mastering the concepts and code patterns described above—Three.js fundamentals, glTF workflow, Cannon.js physics, interaction handling, performance tuning, and debugging utilities—you can efficiently create robust, high‑performance 3D web games. Feel free to share your own improvements in the comments.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Aotu Lab
Aotu Lab, founded in October 2015, is a front-end engineering team serving multi-platform products. The articles in this public account are intended to share and discuss technology, reflecting only the personal views of Aotu Lab members and not the official stance of JD.com Technology.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
