Three.js basics

Ilmari Heikkinen

Who am I?

Ilmari Heikkinen

@ilmarihei | |

Google Chrome Developer Programs Engineer

I write demos, do presentations, write articles

Slides available at

Repo at


Three.js is a JS 3D Engine

Basic setup


Create a WebGLRenderer

      var renderer = new THREE.WebGLRenderer({antialias: true});

Plug it in


And make it pretty

      renderer.setClearColorHex(0xEEEEEE, 1.0);


It gets better, I promise!

Create a Camera

// new THREE.PerspectiveCamera( FOV, viewAspectRatio, zNear, zFar );
var camera = new THREE.PerspectiveCamera(45, width/height, 1, 10000);
camera.position.z = 300;

Make a Scene with a Cube

var scene = new THREE.Scene();
var cube = new THREE.Mesh(new THREE.CubeGeometry(50,50,50),
               new THREE.MeshBasicMaterial({color: 0x000000}));

And render the Scene from the Camera

renderer.render(scene, camera);

At least it isn't empty.


Create a renderer new THREE.WebGLRenderer()

Create a camera new THREE.PerspectiveCamera(fov, aR, n, f)

Create a scene new THREE.Scene()

Create a mesh with a geometry and a material
new THREE.Mesh(new THREE.CubeGeometry(w,h,d),
          new THREE.MeshBasicMaterial({color: 0x000000}))

Add the mesh to the scene scene.add(mesh)

Render the scene renderer.render(scene, camera)

Add the renderer canvas to the page document.body.appendChild(renderer.domElement)


Can we make it move?

Yes we can!

(But it's a manual process.)

      function animate(t) {
        // spin the camera in a circle
        camera.position.x = Math.sin(t/1000)*300;
        camera.position.y = 150;
        camera.position.z = Math.cos(t/1000)*300;
        // you need to update lookAt every frame
        // renderer automatically clears unless autoClear = false
        renderer.render(scene, camera);
        window.requestAnimationFrame(animate, renderer.domElement);
      animate(new Date().getTime());

Isn't it nice?


requestAnimationFrame(function(time){}, element)

It's like setTimeout(f, timeUntilNextFrame)

Except that:



Without light, our scene is very forlorn.

Let's create a Light

      var light = new THREE.SpotLight();
      light.position.set( 170, 330, -160 );

And a lit cube

      var litCube = new THREE.Mesh(
        new THREE.CubeGeometry(50, 50, 50),
        new THREE.MeshLambertMaterial({color: 0xFFFFFF}));
      litCube.position.y = 50;

There we go!

How about shadows?

Three.js has shadow maps.

You need to enable them per-light and per-object.

The shadows only work on SpotLights.

      // enable shadows on the renderer
      renderer.shadowMapEnabled = true;

      // enable shadows for a light
      light.castShadow = true;

      // enable shadows for an object
      litCube.castShadow = true;
      litCube.receiveShadow = true;

Added interest

Let's add a ground plane

  var planeGeo = new THREE.PlaneGeometry(400, 200, 10, 10);
  var planeMat = new THREE.MeshLambertMaterial({color: 0xFFFFFF});
  var plane = new THREE.Mesh(planeGeo, planeMat);
  plane.rotation.x = -Math.PI/2;
  plane.position.y = -25;
  plane.receiveShadow = true;

And make the cube spin

  litCube.position.x = Math.cos(t/600)*85;
  litCube.position.y = 60-Math.sin(t/900)*25;
  litCube.position.z = Math.sin(t/600)*85;
  litCube.rotation.x = t/500;
  litCube.rotation.y = t/800;

Plus a couple of tweaks

Lighting recap

Set mesh material to new THREE.MeshLambertMaterial or new THREE.MeshPhongMaterial

Create a light new THREE.SpotLight(color)

Add the light to the scene scene.add(light)

Turn on shadows if you need them

      renderer.shadowMapEnabled = true;
      light.castShadow = true;
      object.castShadow = true;
      object.receiveShadow = true;


What are shaders

Shaders are small programs that tell WebGL

where to draw


what to draw

Shaders are written in GLSL, the GL Shading Language.

It's kinda like C for graphics.

Vertex shader

Where to draw.

Projects geometry to screen coordinates.

    <script id="vertex" type="text/x-glsl-vert">
      varying float vZ;
      uniform float time;
      void main() {
        vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
        mvPosition.y += 20.0*sin(time*0.5+mvPosition.x/25.0);
        mvPosition.x += 30.0*cos(time*0.5+mvPosition.y/25.0);
        vec4 p = projectionMatrix * mvPosition;
        vZ = p.z;
        gl_Position = p;

Fragment shader

What to draw.

Computes the color of a pixel.

    <script id="fragment" type="text/x-glsl-frag">
      varying float vZ;
      uniform float time;
      uniform vec2 size;
      void main() {
        vec2 d = gl_FragCoord.xy - (0.5+0.02*sin(time))*size;
        float a = sin(time*0.3)*2.0*3.14159;
        d = vec2( d.x*cos(a) + d.y*sin(a),
                 -d.x*sin(a) + d.y*cos(a));
        vec2 rg = vec2(1.0)-abs(d)/(0.5*size)
        float b = abs(vZ) / 160.0;
        gl_FragColor = vec4(rg,b,1.0);

It's very colourful

Shaders and Three.js

Use a ShaderMaterial

var uniforms = {
  time : { type: "f", value: 1.0 },
  size : { type: "v2", value: new THREE.Vector2(width,height) }

var shaderMaterial = new THREE.ShaderMaterial({
  uniforms : uniforms,
  vertexShader : $('#vertex').text(),
  fragmentShader : $('#fragment').text()

var meshCube = new THREE.Mesh(
  new THREE.CubeGeometry(50,50,50, 20,20,20), // 20 segments

Animating shader uniforms

Change the uniform value

      uniforms.time.value += 0.05;

And for the size, add a resize listener

      window.addEventListener('resize', function() {
        uniforms.size.value.x = window.innerWidth;
        uniforms.size.value.y = window.innerHeight;
      }, false);

Useful uses

Bar graph

      var grid = /* 2D Array */
      var barGraph = new THREE.Object3D();

      var max = /* Grid max value */
      var mat = new THREE.MeshLambertMaterial({color: 0xFFAA55});
      for (var j=0; j<grid.length; j++) {
        for (var i=0; i<grid[j].length; i++) {
          var barHeight = grid[j][i]/max * 80;
          var geo = new THREE.CubeGeometry(8, barHeight, 8);
          var mesh = new THREE.Mesh(geo, mat);
          mesh.position.x = (i-grid[j].length/2) * 16;
          mesh.position.y = barHeight/2;
          mesh.position.z = -(j-grid.length/2) * 16;
          mesh.castShadow = mesh.receiveShadow = true;

Bar graph in action

Scatter plot

var scatterPlot = new THREE.Object3D();
var mat = new THREE.ParticleBasicMaterial(
  {vertexColors: true, size: 1.5});

var pointCount = 10000;
var pointGeo = new THREE.Geometry();
for (var i=0; i<pointCount; i++) {
  var x = Math.random() * 100 - 50;
  var y = x*0.8+Math.random() * 20 - 10;
  var z = x*0.7+Math.random() * 30 - 15;
  pointGeo.vertices.push(new THREE.Vertex(new THREE.Vector3(x,y,z)));
  pointGeo.colors.push(new THREE.Color().setHSV(
    (x+50)/100, (z+50)/100, (y+50)/100));
var points = new THREE.ParticleSystem(pointGeo, mat);
scene.fog = new THREE.FogExp2(0xFFFFFF, 0.0035);

Scatter plot in action

Double-click to animate

How to do text

Create a canvas, draw text on it, use as texture.

      var c = document.createElement('canvas');
      c.getContext('2d').font = '50px Arial';
      c.getContext('2d').fillText('Hello, world!', 2, 50);

      var tex = new THREE.Texture(c);
      tex.needsUpdate = true;
      var mat = new THREE.MeshBasicMaterial({map: tex});
      mat.transparent = true;

      var titleQuad = new THREE.Mesh(
        new THREE.PlaneGeometry(c.width, c.height),
      titleQuad.doubleSided = true;

Hello, world!

And the lines?

Create a geometry, add vertices, use to make a THREE.Line.

      function v(x,y,z){ 
        return new THREE.Vertex(new THREE.Vector3(x,y,z)); 
      var lineGeo = new THREE.Geometry();
        v(-50, 0, 0), v(50, 0, 0),
        v(0, -50, 0), v(0, 50, 0),
        v(0, 0, -50), v(0, 0, 50)
      var lineMat = new THREE.LineBasicMaterial({
        color: 0x000000, lineWidth: 1});
      var line = new THREE.Line(lineGeo, lineMat);
      line.type = THREE.Lines;

Coordinate axes

GUI controls

GUI controls

To me, GUI controls have been a pain.

Write your own widgets, write your own onchange handlers, write your own interval change polling logic, style it all, hope it scales, gaaaaaah!

Let's just use DAT.GUI instead.

      var gui = new DAT.GUI();
      gui.add(cube.scale, 'x').min(0.1).max(10).step(0.1);
      gui.add(cube.scale, 'y', 0.1, 10, 0.1);
      gui.add(cube.scale, 'z', 0.1, 10, 0.1);


Scalable cube

But but, where is the pain?

Well, we could do a proxy to control only the currently selected object.

var controller = new THREE.Object3D();
var gui = new DAT.GUI({width: 160});

controller.setCurrent = function(current) {
  this.current = current;
controller.x = gui.add(controller.position, 'x').onChange(function(v){
  controller.current.position.x = v;
// etc.

How to select?

Project a ray into the scene and find intersecting objects.

var projector = new THREE.Projector();
window.addEventListener('mousedown', function (ev){
  if ( == renderer.domElement) {
    var x = ev.clientX;
    var y = ev.clientY;
    var v = new THREE.Vector3((x/width)*2-1, -(y/height)*2+1, 0.5);
    projector.unprojectVector(v, camera);
    var ray = new THREE.Ray(camera.position, 
    var intersects = ray.intersectObjects(controller.objects);
    if (intersects.length > 0) {
}, false);

Great cubistan

Loading models

Model exporters

Look into utils/exporters/

Model loaders

Look into src/extras/loaders/

Huh? So what should I use?

COLLADA loader

new THREE.ColladaLoader().load('models/monster.dae',
function(collada) {
  var model = collada.scene;
  model.scale.set(0.1, 0.1, 0.1);
  model.rotation.x = -Math.PI/2;

Doesn't look too complicated.

Copy-pasted from examples/webgl_collada.html

Along with the code to make it animate.




JavaScript library for 3D graphics

Easy to use


Nice feature set

DAT.GUI for simple GUIs

The End

Ilmari Heikkinen

@ilmarihei | |

Slides available at

Repo at