Qu’est-ce que la 3D sur le web ?
WebGL permet au navigateur de dessiner de la 3D accélérée matériellement via le GPU. C’est un outil puissant, mais l’API brute est verbeuse : vous devez gérer les buffers, écrire des shaders et manipuler les mathématiques vous-même. Three.js encapsule WebGL dans un graphe de scène convivial, vous permettant de décrire des objets, des lumières et des caméras, tout en laissant la bibliothèque gérer le reste.
En conséquence, la 3D sur le web est désormais viable pour les configurateurs de produits, la visualisation de données, le storytelling interactif, les jeux et les expériences immersives WebXR. Ce n’est pas le choix approprié pour tous les sites, mais lorsque le mouvement et l’espace racontent mieux l’histoire qu’une page plane, Three.js est l’outil standard.
Scène, caméra, renderer
Chaque application Three.js repose sur trois objets fondamentaux.
// setup.js
import * as THREE from "three";
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, innerWidth / innerHeight, 0.1, 100);
camera.position.set(0, 0, 3);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
La scène est le conteneur. La caméra définit la vue ; PerspectiveCamera apporte une profondeur réaliste, tandis que OrthographicCamera est utile pour les vues plates ou isométriques. Le renderer dessine la scène sur un canvas. Le fait de limiter le pixel ratio évite que les écrans haute densité ne calculent plus de pixels que nécessaire.
Meshes, géométrie et matériaux
Un objet visible est un mesh : une géométrie pour sa forme et un matériau pour sa surface.
// mesh.js
const cube = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshStandardMaterial({ color: 0x22c55e, roughness: 0.4 }),
);
cube.position.set(0, 0, 0);
scene.add(cube);
Three.js propose de nombreuses géométries intégrées (box, sphere, plane, torus et bien d’autres) ainsi que des matériaux pour obtenir différents rendus. MeshStandardMaterial réagit à la lumière et constitue un bon choix par défaut ; MeshBasicMaterial ignore l’éclairage et est moins coûteux en ressources. Les meshes peuvent être groupés et imbriqués, formant ainsi le graphe de scène via lequel les transformations sont héritées.
Lumières et ombres
Sans lumière, la plupart des matériaux s’affichent en noir.
// lights.js
const ambient = new THREE.AmbientLight(0xffffff, 0.4);
const key = new THREE.DirectionalLight(0xffffff, 2.5);
key.position.set(2, 3, 2);
scene.add(ambient, key);
Les lumières sont des objets dans la scène, leur position et leur type sont donc déterminants. Les ombres nécessitent l’activation de renderer.shadowMap ainsi que le marquage de la lumière, de l’objet et de la surface. Elles sont relativement coûteuses en ressources, utilisez-les donc avec discernement. Chaque lumière ajoute une charge de calcul, essayez donc d’en limiter le nombre.
Modèles et textures
Dans des projets réels, on charge généralement des assets plutôt que de construire la géométrie à la main. glTF est le format 3D standard pour le web.
// load.js
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
const loader = new GLTFLoader();
loader.load("/models/robot.glb", (gltf) => {
scene.add(gltf.scene);
});
Les textures se chargent de manière similaire avec TextureLoader, et RGBELoader ou PMREMGenerator peuvent fournir des cartes d’environnement pour des reflets réalistes. Veillez à garder la taille des modèles et des textures raisonnable, car elles impactent fortement le temps de chargement et la mémoire.
La boucle d’animation
L’animation met à jour la scène et la re-rend à chaque image (frame).
// loop.js
const clock = new THREE.Clock();
renderer.setAnimationLoop(() => {
const delta = clock.getDelta();
cube.rotation.y += delta;
renderer.render(scene, camera);
});
L’utilisation de delta permet de maintenir une vitesse de mouvement constante, quel que soit le taux de rafraîchissement. Lorsque la scène est statique, effectuez le rendu à la demande plutôt qu’en continu : lancez le rendu après un changement, une interaction avec les contrôles ou une animation, et arrêtez-le lorsqu’il ne se passe plus rien. Cela permet d’économiser la batterie et les ressources du GPU sur mobile.
Interaction
Pour rendre des objets cliquables ou interactifs au survol, utilisez un Raycaster.
// pick.js
const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();
addEventListener("pointerdown", (event) => {
pointer.x = (event.clientX / innerWidth) * 2 - 1;
pointer.y = -(event.clientY / innerHeight) * 2 + 1;
raycaster.setFromCamera(pointer, camera);
const hit = raycaster.intersectObjects(scene.children)[0];
if (hit) hit.object.material.color.set(0xf97316);
});
Le raycasting depuis la caméra vers le pointeur renvoie les objets situés en dessous, ce qui est suffisant pour la sélection, la mise en évidence et les interactions simples.
Performance et nettoyage
La 3D est l’élément le plus coûteux que la plupart des sites auront à rendre, la discipline est donc primordiale.
- Libérez (dispose) les géométries, les matériaux et les textures lorsque vous supprimez des objets ; ils résident dans la mémoire du GPU.
- Réutilisez les géométries et les matériaux entre les instances au lieu d’en créer de nouveaux.
- Fusionnez les géométries statiques et utilisez l’instanciation pour les objets répétés.
- Limitez le nombre de lumières et d’objets projetant des ombres.
- Réduisez le pixel ratio sur les écrans haute densité.
- Rendez à la demande pour les scènes statiques.
- Mettez en pause le rendu lorsque le canvas est hors écran.
// dispose.js
mesh.geometry.dispose();
mesh.material.dispose();
scene.remove(mesh);
L’oubli de la libération des ressources (dispose) est la cause la plus fréquente d’une application Three.js qui consomme progressivement toute la mémoire disponible.
React Three Fiber
Avec React, React Three Fiber permet de rendre Three.js de manière déclarative : la scène est ainsi décrite via des composants et s’intègre parfaitement à l’état et au cycle de vie de React.
// Scene.jsx
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";
export function Scene() {
return (
<Canvas>
<ambientLight intensity={0.4} />
<directionalLight position={[2, 3, 2]} intensity={2.5} />
<mesh>
<boxGeometry />
<meshStandardMaterial color="#22c55e" />
</mesh>
<OrbitControls />
</Canvas>
);
}
Les helpers de drei fournissent des contrôles, des environnements et des abstractions courantes. C’est la méthode recommandée pour utiliser Three.js avec React.
Bonnes pratiques
- Limitez le pixel ratio et redimensionnez le renderer en fonction de la fenêtre.
- Réutilisez les géométries et les matériaux ; utilisez l’instanciation pour les objets répétés.
- Libérez les ressources GPU lors de la suppression d’objets.
- Limitez au maximum le nombre de lumières et de sources d’ombres.
- Effectuez le rendu à la demande pour les scènes statiques.
- Testez sur de véritables appareils mobiles de milieu de gamme.
- Prévoyez une solution de repli (fallback) lorsque WebGL n’est pas disponible.
Erreurs courantes
- Oublier de libérer les ressources (
dispose), entraînant des fuites de mémoire GPU. - Effectuer un rendu en continu alors que rien ne change.
- Créer de nouvelles géométries ou des matériaux à chaque frame.
- Ajouter de nombreuses lumières et ombres sans mesurer l’impact sur les performances.
- Charger des modèles et des textures volumineux et non optimisés.
- Tester uniquement sur un ordinateur performant avant de déployer sur mobile.
Et après ?
Three.js ouvre une dimension entièrement nouvelle pour le web. Ajoutez des mouvements chorégraphiés grâce au guide sur l’Animation Avancée, développez avec React Three Fiber si vous utilisez React, et maîtrisez vos ressources avec le guide sur la Performance Web. Ensuite, créez la scène la plus simple possible — un cube éclairé autour duquel vous pouvez orbiter — et faites-la évoluer concept après concept.