Ai

three.js — The Basics, and Why It Fits AI Work

Contents

Notes on three.js, the library behind most “AI made this 3D demo” posts. Everything below is based on 0.186.1 (released September 2026), and every example was run in headless Chromium.

What it is

A JavaScript library for 3D in the browser. Raw WebGL means writing shaders and buffers by hand; three.js wraps that into scenes, cameras and objects, so a few dozen lines get something on screen. Recent versions also support WebGPU.

Four building blocks:

  • Scene: holds objects and lights
  • Camera: where you look from, usually PerspectiveCamera
  • Renderer: does the drawing, WebGLRenderer or WebGPURenderer
  • Mesh: a shape (Geometry) plus a surface (Material)

One HTML file, no build step

An import map pulls three.js straight from a CDN.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!doctype html>
<html>
<body style="margin:0">
<script type="importmap">
{ "imports": {
  "three": "https://cdn.jsdelivr.net/npm/three@0.186.1/build/three.module.js",
  "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.186.1/examples/jsm/"
}}
</script>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 100);
camera.position.set(2, 2, 4);

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

const box = new THREE.Mesh(
  new THREE.BoxGeometry(1, 1, 1),
  new THREE.MeshStandardMaterial({ color: 0x4f8cff })
);
scene.add(box);

scene.add(new THREE.AmbientLight(0xffffff, 0.4));
const light = new THREE.DirectionalLight(0xffffff, 1.5);
light.position.set(3, 5, 2);
scene.add(light);

const controls = new OrbitControls(camera, renderer.domElement);

renderer.setAnimationLoop(() => {
  box.rotation.y += 0.01;
  controls.update();
  renderer.render(scene, camera);
});
</script>
</body>
</html>

Rotating box

  • MeshStandardMaterial needs light. Remove the lights and the box renders black
  • OrbitControls gives you mouse rotate and zoom
  • On window resize, update camera.aspect and call renderer.setSize again

Things you use often

Loading 3D models

Blender exports and most 3D-generation model outputs are .glb files. Open them with GLTFLoader.

1
2
3
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

new GLTFLoader().load('model.glb', (gltf) => scene.add(gltf.scene));

Others

  • Tens of thousands of dots: Points. Many copies of one object: InstancedMesh
  • Clicking objects: Raycaster
  • Post effects such as bloom: EffectComposer
  • WebGPU: import * as THREE from 'three/webgpu' and use WebGPURenderer. Shaders are written in TSL, a JavaScript-based shading syntax
  • In React, use @react-three/fiber

Docs: https://threejs.org/docs/ , examples: https://threejs.org/examples/

Why it fits AI work

LLMs write it well

The library is over ten years old and there is a huge amount of example code and Q&A around it, so LLMs produce fairly accurate three.js code. The result is a single HTML file you can open right away, which is probably why so many AI demos use it.

Looking at embeddings in 3D

Reduce text or image embeddings to three dimensions with UMAP or PCA, plot them with Points, and you can see similar items cluster. You can rotate the view, which helps more than a flat 2D chart.

Here is a test with fake data: 5 classes, 4,000 points each, 20,000 points total.

1
2
3
4
5
6
7
// pos: [x,y,z, x,y,z, ...]  col: [r,g,b, ...]  (Float32Array)
const geo = new THREE.BufferGeometry();
geo.setAttribute('position', new THREE.BufferAttribute(pos, 3));
geo.setAttribute('color', new THREE.BufferAttribute(col, 3));

const points = new THREE.Points(geo, new THREE.PointsMaterial({ size: 0.03, vertexColors: true }));
scene.add(points);

A 20,000-point cloud

On a server with no GPU, using software rendering (SwiftShader), the 20,000 points ran at 34 fps and the single box at 60 fps. A machine with a real GPU should handle far more. I did not test the upper limit.

Viewing 3D generation outputs

Text-to-3D and image-to-3D models mostly output glTF (.glb), PLY point clouds or Gaussian splats. glb and ply open with the built-in loaders. Gaussian splats need a separate library that runs on top of three.js. A depth map from a depth-estimation model can be applied to a plane mesh to give a single photo some depth.

Running models next to it in the browser

MediaPipe, TensorFlow.js, Transformers.js and ONNX Runtime Web all run in the browser too. Rotating an object with hand gestures from a webcam, or driving an avatar from face tracking, works without a server. You share a link and anyone can open it.

Scenes as JSON

A whole scene can be exported to JSON and read back, which is handy when an LLM generates or edits scene data.

1
2
const json = scene.toJSON();
const back = new THREE.ObjectLoader().parse(json);

A scene with one box exports to 2,117 bytes of JSON, and loading it back gives a Scene with the BoxGeometry intact. Because the scene is a parent-child tree, an instruction like “make the left chair red” turns into a few lines of code.

Errors in AI-written three.js code

LLMs often mix in code from old versions. When something breaks, check these first.

Old codeNow
THREE.Geometryremoved, use BufferGeometry
examples/js/... script tagsimport from examples/jsm/...
renderer.outputEncoding = THREE.sRGBEncodingrenderer.outputColorSpace = THREE.SRGBColorSpace
your own requestAnimationFrame looprenderer.setAnimationLoop()

What 0.186.1 actually returns:

1
2
3
4
THREE.Geometry           -> undefined
new THREE.Geometry()     -> TypeError: THREE.Geometry is not a constructor
THREE.sRGBEncoding       -> undefined
THREE.SRGBColorSpace     -> 'srgb'

outputEncoding = THREE.sRGBEncoding is silently ignored. The renderer already defaults to sRGB, so usually nothing looks wrong. The real problem is texture.encoding = THREE.sRGBEncoding from the same era of code. It is ignored too, and textures come out washed out. Use texture.colorSpace = THREE.SRGBColorSpace instead.

When asking an AI for three.js code, add “latest three.js, ES modules, import map”. Most of these problems go away.

There is no physics engine or editor. For physics, add cannon-es or Rapier. For something game-sized, Babylon.js may be easier.