Getting Started with the HoloPlayer Three.js Library

This tutorial shows you how to set up your first Three.js scene for the HoloPlayer using our HoloPlay.js library! To keep things consistent and to show the differences in how this works as opposed to our Unity SDK, I’m going to make (roughly) the same scene in my Unity HoloPlay SDK tutorial. In this slightly adjusted version, 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!


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, get the HoloPlay JavaScript library, which you can download from our forum.

Now we can 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.

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 and I’ll cover that here, but if you’re on Windows, the process is much more involved and you should follow this tutorial.

Open terminal, and navigate to the folder that has our project in it using the “cd” command.

From this folder, we want to launch a local PHP server by entering the command:

php -S 127.0.0.1:8080

This allows us to go to the url 127.0.0.1:8080 in our browser and access our project.

Step 2: Making our scene

Now we’re finally ready to start coding! Open your text editor of choice (I use Brackets, but Sublime is a popular one as well). Open the ThreeJSTutorial folder in your 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!

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>
HoloPlayerTutorial
</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. All the CSS is doing is making a canvas element that will 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;
var cubeMovements;

//Initialize our variables
function init(){

}

//Update game logic
function update(){

}

//Render the scene
function draw(){

}

//Game loop
function RunApp(){
requestAnimationFrame(RunApp);
update();
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(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);
}

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 HoloPlayer. 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/HoloPlayer. It’s just that our scene is empty!

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 in the upper right-hand side of your browser window a button asking you to “Load HoloPlay Configuration JSON.” We need to do this because each HoloPlayer is slightly different, and so we need to upload it’s particular configuration. Anyone who visits your webpage will see this button, and will be prompted to load the configuration. Once they do so, the data is stored to their local storage memory, and will be automatically loaded, so they will not see the button anymore. Let’s load in our configuration now by clicking on the button, which will open a file loader interface.

Navigate to your HoloPlayer connection, which will have a folder called “holoPlaySDK_calibration.” Inside, there is a JSON file called “holoPlayConfig.json.” Click on that and press “Open” and the configuration will be loaded into the local storage of your browser. If at any time you need to change the calibration that is stored in memory, pressing the “tilde” key on your keyboard will reopen this file explorer interface, and you can load in a new JSON file.

With our renderer now 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(0,i - 1, 0);
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 stacked on top of each other, 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 HoloPlayer!

There we have it, something drawing in our HoloPlayer from the browser! As a final step, let’s give the scene a little life by moving the cubes back and forth at random speeds by making use of our “cubeMovements” variable. Right after we initialize our cube array in “init,” lets initialize cubeMovements as an empty array as well. Then, in the for loop, we’ll set the values to a random number times a scalar to slow down the movement a bit.

cubes = [];
cubeMovements = [];
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(0,i - 1, 0);
scene.add(cubes[i]);
  cubeMovements.push(Math.random() * 0.1);
}

Now, we’ll use this array in our “update” function to move the cubes on the X-axis. When they get too far along on the X-axis, we switch the sign of their movement to make them go the other way.

//Update game logic
function update(){
for(var i = 0; i < cubes.length; i++){
cubes[i].position.x += cubeMovements[i];
if(Math.abs(cubes[i].position.x) > 3){
cubeMovements[i] = -cubeMovements[i];
}
}
}

This will make our cubes bounce back and forth in the screen.

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

<!DOCTYPE html>
<html>
<head>
<title>
HoloPlayerTutorial
</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;
var cubeMovements;

//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 = [];
cubeMovements = [];
        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(0,i - 1, 0);
scene.add(cubes[i]);

cubeMovements.push(Math.random() * 0.1);
}
}

//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();
});

//Update game logic
function update(){
for(var i = 0; i < cubes.length; i++){
cubes[i].position.x += cubeMovements[i];
if(Math.abs(cubes[i].position.x) > 3){
cubeMovements[i] = -cubeMovements[i];
}
}
}

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

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

init();
RunApp();

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

Questions? Dying to share your glorious holographic creations? Head on over to the Forum!

If you want a HoloPlayer One of your own, we’ll be selling very limited quantities of prototype systems at this link!

If you have any other questions or feedback, email future@lookingglassfactory.com. To the future!