Skip to content

Getting Started with the Three.js Library for the Looking Glass

This guide shows you how to set up your first Three.js scene for the Looking Glass using our HoloPlay.js library. In this guide, we'll make three cubes that stack on top of each other and that move from left to right. You can see the project running here.

cubes

Step 1: Setup

For those who aren't familiar, Three.js is a really awesome 3D web library that is built on top of WebGL.

You'll need to download it before we start, so go to their site and click the "download" button at the top left -- and check out some of the cool stuff made using the framework while you're there!

Once you've downloaded Three.js, unzip the folder. Next, download the Holoplay JavaScript library here.

Now it's time to start making our own project

Let's make a folder on our desktop called "ThreeJSTutorial." This will be the folder for our Three.js project.

In that folder, we're going to make another folder and call it "js." This is the folder in which we'll put all our JavaScript files that our Three.js project will need to use, like the HoloPlay JavaScript library and Three.js itself.

js folder js folder contents

Drag your holoplay.js and three.js scripts into this folder so that they're ready to use in our HTML.

We're almost ready to start making our scene, but before we do so we want to set up a local host on our computer. This is important because most browsers don't allow you to upload files directly from your laptop. This process is pretty painless on Mac -- you can see this tutorial on setting up a PHP local host -- but if you're on Windows, the process is much more involved and you should follow this tutorial. You can also install a Linux bash shell on Windows, which is what I will be doing.

Whatever your approach, open a local host from the "ThreeJSTutorial" folder on your desktop so we can see what we're working on.

Step 2: Making our scene

Now we're finally ready to start coding! Open the ThreeJSTutorial folder in your text editor, and create a new file in the root folder (NOT the folder called "js") called index.html. This will be the main script for running our 3D webpage!

index

We'll need to start with a little bit of HTML and CSS boilerplate, but this code is very simple, mostly because you don't really use a lot of CSS with Three.js. Here is that code snippet:

<!DOCTYPE html>
<html>
  <head>
    <title>
      Looking Glass Tutorial
    </title>
    <style>
      body 
      { 
        margin: 0; 
      }
      canvas 
      {
        width: 100%; 
        height: 100%; 
      };
    </style>
  </head>

  <body>

  </body>
</html>

The <head> segment isn't really too important here, it's really just setting the name of our project and the CSS. The CSS simply makes the canvas element take up our entire browser page, which will be the canvas used by Three.js. Where we'll be focusing our attention is in the body segment of this script.

First, we need to include our three.js and holoplay.js files. After those are included, we can start writing the code for our scene inside another <script> tag.

<body>
  <script src="js/three.js"></script>
  <script src="js/holoplay.js"></script>
  <script>
    //Our scene code here
  </script>
</body>

This will import the libraries we need so we can use them in our scene logic. Let's put in our basic scene logic now:

<script>
  //Basic elements for a Three.js/HoloPlay scene
  var scene, camera, renderer, holoplay;

  //Lighting elements
  var directionalLight;
  var ambientLight;

  //Scene objects
  var cubeGeometry;
  var cubeMaterial;
  var cubes;

  //Initialize our variables
  function init(){

  }

  //Render the scene
  function draw(){

  }

  //Game loop
  function RunApp(){
    requestAnimationFrame(RunApp);
    draw();
  }

  init();
  RunApp();

</script>

We'll start by initializing our scene variables: scene, camera, renderer, and holoplay.

//Initialize our variables
function init(){
  scene = new THREE.Scene();

  camera = new THREE.PerspectiveCamera(13.5, window.innerWidth/window.innerHeight, 0.1, 1000);
  camera.position.set(0,0,20);

  renderer = new THREE.WebGLRenderer();
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);

  holoplay = new HoloPlay(scene, camera, renderer);
}

Our scene is just a typical Three.js scene, which is essentially an object to store all our other objects inside of. Then there's our camera, which is a perspective camera with quite a narrow field of view, which looks better, in general, on the Looking Glass. We set it's position to 20 on the Z-axis to ensure that, when we draw stuff at (0,0,0), we'll be able to see it. Finally comes our renderer, which uses WebGL to render 3D geometry on the webpage. We append the DOM element of this object to the document body so that it actually draws to our browser page.

We pass all of these to our HoloPlay object's constructor so that it has a reference to them when converts what they normally draw into a 3D image!

The final thing we want to add is a nice function that resets our objects when the window is resized. To do that, we add an event listener to our document that executes a function resetting some of the key variables on our camera and our renderer. So, beneath the "init" function, we put:

//Resize window on size change
window.addEventListener('resize', function(){
  var width = window.innerWidth;
  var height = window.innerHeight;

  renderer.setSize(width, height);

  camera.aspect = width/height;
  camera.updateProjectionMatrix();
});

With our scene set up and ready to go, we can now add the objects that will populate our scene. Before we do though, we're going to add one line to our "draw" function so that we can see what's going on in our scene. The line we're going to add is:

//Render the scene
function draw(){
  holoplay.render();
}

You won't see anything change, but this is actually rendering our scene to our monitor/Looking Glass. It's just that our scene is empty!

empty scene

Once we have stuff in our scene, we'll be able to see it because of this function call. Before we do so, however, you'll notice the alert that popped up when you opened the web page that tells us that we don't have drivers. We need to install these on our computers in order to read calibration from the Looking Glass. Once we install them once, however, we won't have to do it again!

Press OK and you should download an installer for Windows or OS X (it reads what system your on and gives you the correct one). Once it's downloaded, click on the installer.

Installer

The installer will run, though you may need to give it permission to make changes, and once it has (it should only take a few seconds) you can click the "Close" button.

Installer 2

With our drivers installed and our renderer all set up, we can populate our scene with objects we can actually see. We want to draw a bunch of cubes in our scene, but in order for those cubes to look nice, we need to have some lights. Otherwise, if we use anything but the simplest material, we won't be able to see our cubes! So let's add two types of lights that we declared above: a directional light and an ambient light.

The directional light works kind of like the sun, pointing to our world in a particular direction. The sides of objects facing the directional light will be brighter than those facing away from it. In fact, those sides NOT hit by the directional light will be black, which is where our ambient light comes in. Ambient light adds light to all sides of objects regardless of what direction they're facing or where they are in the scene. Generally, you want this to be dimmer than your directional light. So let's add those into our scene now.

//Initialize our variables
function init(){
  scene = new THREE.Scene();

  camera = new THREE.PerspectiveCamera(12.5, window.innerWidth/window.innerHeight, 0.1, 1000);
  camera.position.set(0,0,20);

  renderer = new THREE.WebGLRenderer();
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);

  holoplay = new HoloPlay(scene, camera, renderer);

  directionalLight = new THREE.DirectionalLight(0xFFFFFF, 1);
  directionalLight.position.set (0, 1, 2);
  scene.add(directionalLight);

  ambientLight = new THREE.AmbientLight(0xFFFFFF, 0.4);
  scene.add(ambientLight);
}

We make a new THREE.DirectionalLight, and we set its color to be white (0xFFFFFF in hex code terms) and its intensity to 1. Then we add it to our scene by calling scene.add(). If we don't do this to objects, they won't exist in our scene, so we need to add any objects we want to use, including our lights! Our ambient light is also white, but has a lower intensity of 0.4. We also add this to our scene.

If you refresh your page, you'll see that there's still nothing there. But our scene is now set up such that once there is something there, we'll be able to see it. So let's add in some cubes!

//Initialize our variables
function init(){
  scene = new THREE.Scene();

  camera = new THREE.PerspectiveCamera(12.5, window.innerWidth/window.innerHeight, 0.1, 1000);
  camera.position.set(0,0,20);

  renderer = new THREE.WebGLRenderer();
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);

  holoplay = new HoloPlay(scene, camera, renderer);

  directionalLight = new THREE.DirectionalLight(0xFFFFFF, 1);
  directionalLight.position.set (0, 1, 2);
  scene.add(directionalLight);

  ambientLight = new THREE.AmbientLight(0xFFFFFF, 0.4);
  scene.add(ambientLight);

  cubes = [];

  cubeGeometry = new THREE.BoxGeometry(1,1,1);
  cubeMaterial = new THREE.MeshLambertMaterial();

  for(var i = 0; i < 3; i++){
    cubes.push(new THREE.Mesh(cubeGeometry, cubeMaterial));
    cubes[i].position.set(1 - i, 1 - i, 1 - i);
    scene.add(cubes[i]);
  }
}

First, we set our "cubes" variable to equal an empty array that we'll soon populate with three cubes. We then create the geometry and the material we'll use to make the cubes look the way we want. The box geometry just means that the cube will be in the shape of a box, and we are setting the box to be of size (1,1,1). Then we're making a new Lambert material (white by default) which will allow our cubes to react to light and give them a matte look. With these variables set up, we're ready to use them to make a few cubes.

In our for loop, we create new cubes and we add them to our array. We then set their position such that they are staggered from bottom left and back to top right and forward, and add them into our scene. Now, if we save our script and refresh our browser page, we'll see our cubes are there, forming one white tower in our Looking Glass!

cubes

And there you have it, your first ever truly 3D website, drawing on the Looking Glass! Here is the complete code of the index.html file for reference:

<!DOCTYPE html>
<html>
  <head>
    <title>
      Looking Glass Tutorial
    </title>
    <style>
      body 
      { 
        margin: 0; 
      }
      canvas 
      {
        width: 100%; 
        height: 100%; 
      };
    </style>
  </head>

  <body>
    <script src="js/three.js"></script>
    <script src="js/holoplay.js"></script>

<script>
      //Basic elements for a Three.js/HoloPlay scene
      var scene, camera, renderer, holoplay;

      //Lighting elements
      var directionalLight;
      var ambientLight;

      //Scene objects
      var cubeGeometry;
      var cubeMaterial;
      var cubes;

      //Initialize our variables
      function init(){
        scene = new THREE.Scene();
        camera = new THREE.PerspectiveCamera(12.5, window.innerWidth/window.innerHeight, 0.1, 1000);
        camera.position.set(0,0,20);
        renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);
        holoplay = new HoloPlay(scene, camera, renderer);
        directionalLight = new THREE.DirectionalLight(0xFFFFFF, 1);
        directionalLight.position.set (0, 1, 2);
        scene.add(directionalLight);
        ambientLight = new THREE.AmbientLight(0xFFFFFF, 0.4);
        scene.add(ambientLight);
        cubes = [];
        cubeGeometry = new THREE.BoxGeometry(1,1,1);
        cubeMaterial = new THREE.MeshLambertMaterial();
        for(var i = 0; i < 3; i++){
          cubes.push(new THREE.Mesh(cubeGeometry, cubeMaterial));
          cubes[i].position.set(1 - i, 1 - i, 1 - i);
          scene.add(cubes[i]);
        }
      }

      //Resize window on size change
      window.addEventListener('resize', function(){
        var width = window.innerWidth;
        var height = window.innerHeight;
        renderer.setSize(width, height);
        camera.aspect = width/height;
        camera.updateProjectionMatrix();
      });

      //Render the scene
      function draw(){
        holoplay.render();
      }

      //Game loop
      function RunApp(){
        requestAnimationFrame(RunApp);
        draw();
      }

      init();
      RunApp();

    </script>
  </body>
</html>

If you have any other questions or feedback, email [email protected]