iducationducation
IndexArticlesFormationsProfilOutilsBibliothech
N°014 — 2026
Navigation
01Index02Articles03Formations04Profil05Outils06Bibliothech
N°014 — 2026
iducationducation
IndexArticlesFormationsProfilOutilsBibliothech
N°014 — 2026
Navigation
01Index02Articles03Formations04Profil05Outils06Bibliothech
N°014 — 2026
Formations
JavaScript · Intermédiaire

Three.js

Créer de la 3D dans le navigateur de zéro : scène, modèles STL/GLTF, animation au scroll, shaders, post-processing et portfolio 3D animé complet.

Three.jsJavaScriptGSAPGLSLWebGL
01Introduction à Three.js02Géométries, matériaux et lumières03Charger des modèles 3D — STL et GLTF04Animer au scroll — Three.js + GSAP05Post-processing et effets visuels06Shaders GLSL et systèmes de particules07Portfolio 3D animé — projet complet
Chapitre 1·20 min

Introduction à Three.js

WebGL est une API JavaScript qui donne accès direct au GPU pour dessiner en 2D et 3D dans un <canvas>. Sans abstraction, voici ce qu'il faut écrire pour afficher un triangle :

// ~150 lignes de WebGL brut pour un triangle
const gl = canvas.getContext('webgl')
const vertexShader = gl.createShader(gl.VERTEX_SHADER)
// ... compiler le shader GLSL
// ... créer le program
// ... lier les buffers
// ... configurer les attributs
// ... enfin : gl.drawArrays(gl.TRIANGLES, 0, 3)

Three.js réduit ça à quelques lignes. Il gère le contexte WebGL, la compilation des shaders, les buffers GPU — en exposant une API orientée objet lisible.

Installation

npm install three

Types TypeScript :

npm install -D @types/three

Les trois éléments fondamentaux

Toute scène Three.js repose sur trois objets obligatoires.

Scene

Conteneur de tout ce qui existe dans le monde 3D. On y ajoute les objets, les lumières, les caméras.

import * as THREE from 'three'
 
const scene = new THREE.Scene()
scene.background = new THREE.Color('#0a0a0a')

Camera

Définit le point de vue. La plus courante est PerspectiveCamera — elle simule la vision humaine avec perspective.

const camera = new THREE.PerspectiveCamera(
  75,                              // FOV (field of view) en degrés
  window.innerWidth / window.innerHeight,  // aspect ratio
  0.1,                             // near plane — rien de plus proche n'est rendu
  1000                             // far plane — rien de plus loin n'est rendu
)
 
camera.position.set(0, 0, 5)  // reculer la caméra sur l'axe Z

Le FOV contrôle l'angle de vision. 50-75° est naturel. Au-delà de 90° l'effet fish-eye devient prononcé.

Renderer

Dessine la scène dans un <canvas> en utilisant le GPU.

const renderer = new THREE.WebGLRenderer({
  antialias: true,   // lissage des bords
  alpha: true,       // fond transparent (pour superposer sur du HTML)
})
 
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))  // max 2x pour la perf
renderer.shadowMap.enabled = true
renderer.shadowMap.type = THREE.PCFSoftShadowMap
 
document.body.appendChild(renderer.domElement)  // ajouter le canvas au DOM

setPixelRatio(Math.min(devicePixelRatio, 2)) — les écrans Retina ont un ratio de 2 ou 3. À 3, le renderer trace 9 pixels par pixel "CSS". On plafonne à 2 pour garder de bonnes performances.

Premier objet : un cube

Un objet Three.js = une Geometry (forme) + un Material (apparence) = un Mesh.

// Géométrie — les données de forme
const geometry = new THREE.BoxGeometry(1, 1, 1)
 
// Matériau — apparence visuelle
const material = new THREE.MeshStandardMaterial({
  color: '#cdff4f',
  roughness: 0.3,
  metalness: 0.1,
})
 
// Mesh = geometry + material
const cube = new THREE.Mesh(geometry, material)
scene.add(cube)

MeshStandardMaterial est un matériau PBR (Physically Based Rendering) — il réagit aux lumières de façon réaliste. Sans lumière dans la scène, ce matériau rend tout noir.

// Lumière directionnelle — comme le soleil
const light = new THREE.DirectionalLight('#ffffff', 2)
light.position.set(5, 5, 5)
scene.add(light)
 
// Lumière ambiante — éclairage de base pour éviter le noir total
const ambient = new THREE.AmbientLight('#ffffff', 0.5)
scene.add(ambient)

La render loop

Three.js ne re-dessine pas automatiquement. Il faut une boucle qui appelle renderer.render() à chaque frame.

function animate() {
  requestAnimationFrame(animate)
 
  // Faire tourner le cube
  cube.rotation.x += 0.01
  cube.rotation.y += 0.01
 
  renderer.render(scene, camera)
}
 
animate()

requestAnimationFrame appelle la fonction avant le prochain repaint du navigateur — typiquement 60 fois par seconde, ou selon le taux de rafraîchissement de l'écran.

Delta time — animations indépendantes du framerate

Sur un écran 60hz, la rotation de 0.01 par frame = 0.6 radians/seconde. Sur un écran 120hz, ça double. La solution : utiliser le temps écoulé.

const clock = new THREE.Clock()
 
function animate() {
  requestAnimationFrame(animate)
 
  const delta = clock.getDelta()  // secondes depuis le dernier frame
 
  cube.rotation.y += delta * 0.5  // 0.5 radian par seconde, peu importe le fps
 
  renderer.render(scene, camera)
}

Gestion du resize

Quand la fenêtre est redimensionnée, l'aspect ratio de la caméra et la taille du renderer doivent être mis à jour.

window.addEventListener('resize', () => {
  // Mettre à jour la caméra
  camera.aspect = window.innerWidth / window.innerHeight
  camera.updateProjectionMatrix()  // obligatoire après changement d'aspect
 
  // Mettre à jour le renderer
  renderer.setSize(window.innerWidth, window.innerHeight)
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})

Sans updateProjectionMatrix(), la caméra continue d'utiliser l'ancien aspect ratio — les objets apparaissent étirés.

Code complet — setup minimal

import * as THREE from 'three'
 
// Scène
const scene = new THREE.Scene()
scene.background = new THREE.Color('#111111')
 
// Caméra
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
)
camera.position.z = 5
 
// Renderer
const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
document.body.appendChild(renderer.domElement)
 
// Lumières
scene.add(new THREE.AmbientLight('#ffffff', 0.5))
const dirLight = new THREE.DirectionalLight('#ffffff', 2)
dirLight.position.set(5, 5, 5)
scene.add(dirLight)
 
// Objet
const cube = new THREE.Mesh(
  new THREE.BoxGeometry(1, 1, 1),
  new THREE.MeshStandardMaterial({ color: '#cdff4f' })
)
scene.add(cube)
 
// Resize
window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight
  camera.updateProjectionMatrix()
  renderer.setSize(window.innerWidth, window.innerHeight)
})
 
// Boucle de rendu
const clock = new THREE.Clock()
function animate() {
  requestAnimationFrame(animate)
  cube.rotation.y += clock.getDelta() * 0.5
  renderer.render(scene, camera)
}
animate()

Système de coordonnées

Three.js utilise un système de coordonnées main droite :

       Y
       │
       │
       └────── X
      /
     Z
  • X positif → droite
  • Y positif → haut
  • Z positif → vers vous (hors de l'écran)

La caméra regarde vers -Z par défaut. C'est pourquoi on recule la caméra à z = 5 pour voir un objet placé à l'origine.

OrbitControls — navigation dans la scène

Pour explorer librement la scène avec la souris pendant le développement.

import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
 
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true    // inertie au relâchement
controls.dampingFactor = 0.05
 
// Dans la render loop — obligatoire avec damping
function animate() {
  requestAnimationFrame(animate)
  controls.update()  // ← nécessaire pour le damping
  renderer.render(scene, camera)
}

OrbitControls n'est pas dans le core de Three.js — c'est dans three/examples/jsm/. Ces helpers sont dans le package npm mais dans un sous-dossier séparé.


La scène de base est en place. Le chapitre suivant explore les géométries, matériaux et lumières en détail — les outils pour construire des scènes visuellement riches.

Suivant
Géométries, matériaux et lumières

Développeur fullstack passionné. J'apprends en construisant et je documente tout — front, back, outils. Le code s'apprend mieux en public.

Naviguer

IndexTous les articlesFormationsProfilOutilsBibliothech

Ailleurs

GitHub RSS

Newsletter

Les articles, libs et découvertes. Une fois par semaine, pas plus.

© 2026 William LoreeConçu & codé à la main