Esc
 Naviguer  Ouvrir Esc Fermer
Aller au contenu

16.3 — 3D, jeu, créatif

🎯 Objectif : pouvoir intégrer un canvas, une scène 3D ou un effet shader sur le web sans être un développeur jeu — comprendre les briques et savoir où s’arrêter.

À l'issue de cet axe, tu sauras :

  • Choisir entre Canvas 2D, SVG, WebGL, Three.js et WebGPU selon le besoin
  • Construire une scène 3D minimale en Three.js et React Three Fiber
  • Optimiser les perfs (draw calls, géométrie, GPU instancing)
  • Rendre la 3D accessible (contrôles clavier, fallback statique)
  • Comprendre où WebGPU change la donne en 2026

Avancé 11 min prérequis : axes 1-14 maîtrisés

APIQuand l’utiliser
CSS / SVGAnimations UI, infographies vectorielles, < 500 éléments
Canvas 2DCharts custom, mini-jeux 2D, drawing tools, > 500 sprites
WebGL / Three.js3D, effets shader, visualisations complexes, configurateurs produit
React Three Fiber (R3F)3D dans une app React
WebGPUCompute, particules massives, rendering moderne — futur défaut

Règle : commence par CSS/SVG. Passe au Canvas 2D quand le DOM rame. Passe à WebGL quand tu as besoin de 3D ou de shaders. WebGPU pour les cas avancés et le compute.


  • Un élément <canvas> + une CanvasRenderingContext2D.
  • Tu dessines via ctx.fillRect, ctx.beginPath, ctx.drawImage, etc.
  • Pas de DOM par sprite → tu peux dessiner 10 000 objets à 60 FPS.
  • Tu redessines à chaque frame (requestAnimationFrame).
  • Pas accessible par défaut — il faut un fallback.
const canvas = document.querySelector('canvas')!;
const ctx = canvas.getContext('2d')!;
let particles = Array.from({ length: 1000 }, () => ({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
vx: (Math.random() - 0.5) * 2,
vy: (Math.random() - 0.5) * 2,
}));
function frame() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (const p of particles) {
p.x = (p.x + p.vx + canvas.width) % canvas.width;
p.y = (p.y + p.vy + canvas.height) % canvas.height;
ctx.fillRect(p.x, p.y, 2, 2);
}
requestAnimationFrame(frame);
}
frame();
  • Charts custom (dépasser les capacités d’ECharts / Chart.js).
  • Mini-jeux 2D simples (puzzle, plateformer léger).
  • Drawing tools (Excalidraw, tldraw).
  • Visualisations data dynamiques (network graph très dense).
LibPour quoi
KonvaScène 2D avec hit detection (drag, click)
PixiJS2D + WebGL accelerated, jeux 2D modernes
D3Data viz (souvent SVG, mais compatible Canvas)
Excalidraw / tldrawOutils dessin clé en main

WebGL est l’API GPU navigateur. Tu écris des shaders GLSL (vertex + fragment) et tu uploads des buffers de données. Très puissant, très verbeux.

// Fragment shader — couleur uniforme
precision mediump float;
uniform vec3 uColor;
void main() {
gl_FragColor = vec4(uColor, 1.0);
}

Pour une scène 3D simple, tu écris ~200 lignes. Pas raisonnable pour 95 % des cas.

Three.js est la lib 3D web. Elle abstrait WebGL, gère caméras, lumières, géométries, animations.

import * as THREE from 'three';
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, innerWidth / innerHeight, 0.1, 1000);
camera.position.z = 5;
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
const cube = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshStandardMaterial({ color: 0x44aa88 })
);
scene.add(cube);
scene.add(new THREE.DirectionalLight(0xffffff, 1));
scene.add(new THREE.AmbientLight(0x404040));
function animate() {
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
ConceptRôle
SceneConteneur de tous les objets
CameraPerspective ou Orthographic
RendererWebGL / WebGPU — produit l’image
MeshGéométrie + matériau
GeometryPosition des vertices (Box, Sphere, GLTF…)
MaterialPropriétés de surface (couleur, métal, rugosité)
LightDirectional, Point, Ambient, Spot
OrbitControlsCaméra interactive (clic-tirer pour tourner)

React Three Fiber (R3F) wrap Three.js en composants React. L’option recommandée 2026 dans une app React.

import { Canvas } from '@react-three/fiber';
import { OrbitControls } from '@react-three/drei';
export default function Scene() {
return (
<Canvas camera={{ position: [3, 3, 3] }}>
<ambientLight intensity={0.4} />
<directionalLight position={[5, 5, 5]} />
<mesh rotation={[0.4, 0.4, 0]}>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="hotpink" />
</mesh>
<OrbitControls />
</Canvas>
);
}
PackageQuoi
@react-three/dreiHelpers : OrbitControls, environnements, GLTF loader, text
@react-three/postprocessingBloom, DoF, glitch — effets post-process
@react-three/rapierPhysique 3D (rigid bodies, joints)
@react-three/cannonPhysique alternative (un peu vieillissante)
levaUI debug pour ajuster les paramètres en live
import { useGLTF } from '@react-three/drei';
function Model() {
const { scene } = useGLTF('/duck.glb');
return <primitive object={scene} />;
}

Format glTF / GLB : standard 2026. Compresser via Draco ou Meshopt pour des fichiers 5-20× plus légers.


Frame budget @ 60 FPS = 16,6 ms
@ 120 FPS = 8,3 ms

Si tu dépasses, le navigateur skip une frame → ressenti saccadé.

AxeLevier
Draw callsMoins d’appels GPU = mieux. Merger les meshes statiques (BufferGeometryUtils.mergeGeometries)
GéométrieCompresser, simplifier (Draco, decimate Blender)
MaterialTexture atlases, mipmaps, partager les materials
GPU instancing1000 cubes identiques en 1 draw call (InstancedMesh)
Frustum cullingNe pas rendre ce qui est hors caméra (Three.js le fait par défaut)
LOD (Level of Detail)Modèles plus simples quand loin
ShadowsCoûteuses — désactiver ou limiter à un seul light
  • Chrome DevTools → Performance → enregistre une session.
  • stats.js — overlay FPS / ms / mem.
  • r3f-perf — overlay R3F dédié.
  • Spector.js — extension Chrome pour debug WebGL frame par frame.
Page produit → 1 modèle GLB principal (~500 KB compressé Draco)
→ 5 textures PBR (basecolor, normal, roughness, metallic, AO)
→ 1 environnement HDR pour reflets
→ 8000 vertices total
→ 60 FPS sur Mac M3 → ✅
→ 30-45 FPS sur Android milieu de gamme → 🔵 acceptable

WebGPU est l’API graphique nouvelle génération. Universellement supportée en 2026 (Chrome, Edge, Firefox, Safari).

AspectWebGLWebGPU
ModèleStateful (OpenGL ES)Stateless (Vulkan-like)
PerformancesOK2-5× sur la plupart des cas
Multi-threadingNonOui (workers)
Compute shadersNon (hack via fragment)Oui natif
Shading languageGLSLWGSL
Bindings clairsLourdsÉlégants
  • Compute (simulations, ML inference, rendu non-graphique).
  • Particules massives (>1M).
  • Volumetric rendering (smoke, fluids).
  • GPU-accelerated data viz très lourde.

Three.js a un WebGPURenderer officiellement stable depuis 2024. Tu peux switch :

import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';
const renderer = new WebGPURenderer({ antialias: true });
await renderer.init();

En 2026, WebGPU est l’option par défaut sur projets neufs. WebGL reste pertinent en fallback sur appareils anciens.


Une scène 3D pure est inaccessible par défaut :

  • Pas de focus clavier.
  • Pas lue par lecteur d’écran.
  • Souris/tactile uniquement.
<canvas role="img" aria-label="Configurateur 3D du sac à dos modèle Adventure"></canvas>
<!-- Description longue + alternatives -->
<div class="sr-only">
<h2>Détail du modèle</h2>
<ul>
<li>Couleur : <select id="color">...</select></li>
<li>Taille : <select id="size">...</select></li>
<li>Matériau : <select id="material">...</select></li>
</ul>
</div>
<!-- Capture statique en fallback -->
<noscript><img src="product-hero.jpg" alt="Sac à dos Adventure rouge"></noscript>
RèglePourquoi
aria-label sur le <canvas>Lecteur d’écran l’identifie
Contrôles UI doublés (boutons / selects)L’utilisateur clavier peut tout faire
Snapshot statique SEO + a11ySi JS off, toujours quelque chose à voir
Animations désactivables (prefers-reduced-motion)Vestibulaire
Texte lisible dans la scène (pas seulement les labels 3D)Les labels 3D ne sont pas indexés
Contrastes suffisants entre élémentsDaltoniens, faible vision

Beaucoup de projets « 3D » seraient mieux servis par autre chose :

BesoinSolution moins lourde
« Un truc qui bouge »Animation CSS / Lottie
Carte interactiveMapbox, MapLibre, Leaflet
Schéma 3D simpleSketchfab embed, modèle GLB statique
Visualisation dataPlotly 3D, deck.gl
Mini-jeu 2DPixiJS / Phaser
AR mobileWebXR / model-viewer
Effets « hero » landingSpline (no-code), Rive

Spline permet de créer des scènes 3D dans le navigateur et d’exporter en composant React.

import Spline from '@splinetool/react-spline';
export default function Hero() {
return <Spline scene="https://prod.spline.design/xxxxxx/scene.splinecode" />;
}

Idéal pour : sections hero marketing, demos produit. Pas pour : configurateurs avec logique métier complexe.


SymptômeSolution
Modèle 50 MB en GLB sans compressionDraco / Meshopt → 1-3 MB typiquement
200 draw calls par frameMerge meshes statiques, GPU instancing
Shadows partout1 directionnal light shadow max, désactiver pour mobile
Pas de fallback statiqueRender-blocking pour Lighthouse, mauvais SEO
Animation infinie sur la homeDésactiver après quelques secondes ou sur scroll
Pas de Suspense / loading stateEcran blanc 3 sec, UX cassée
Caméra qui bouge toute seule + parallaxVestibulaire — donner un toggle
Chargement synchrone du modèle au mountLazy-load + skeleton

Tu construis un dashboard avec ~30 graphiques classiques (line, bar, pie). Quelle techno ?
Sur une app React, tu veux ajouter un configurateur 3D produit. Quelle stack en 2026 ?
Tu charges un modèle GLB de 18 MB. Lighthouse t'engueule, mobile rame. Levier prioritaire ?
Tu mets une scène 3D Three.js sur la home publique. Lighthouse a11y score chute à 78. Pourquoi ?


Suite : 16.4 — IA appliquée au web — LLM, RAG, embeddings, agents.