Bounce Some Particles with three.js

I gave a talk at JSConf China, for which I made a little demo in three.js. I wanted to be able to explain every line of code in ~10 minutes to people with no graphics knowledge. JSConf China is not a multi-track conference, everyone attending the conference hears the talk so I stuck to the basics.

Btw, the slides to my talk is here.

Demo with animation and controls

The demo was extremely simple. It basically:
1. creates a three.js scene with init()
2. adds some spheres to the scene with fillScene()
3. animate and add controls to the scene with animate()

I suggest you read this with the three.js documentation.

index.html

First of all, page setup. Get the three.min.js and OrbitControls.js from three.js GitHub repo and reference it on your page. Other than that, all you need to have is a scripts.js to write the JavaScript and a canvas with an id so we can grab it easily.

<!DOCTYPE html>
<html>
    <head>
        <meta charset=utf-8>
        <title>Particles</title>
        <style>
            body { margin: 0px; overflow: hidden; }
        </style>
    </head>
    <body>
        <div id="canvas"></div>
        <script src="../three/three.min.js"></script>
        <script src="../three/OrbitControls.js"></script>
        <script src="js/scripts.js"></script>
    </body>
</html>

Now we start writing the JavaScript.

Writing scripts.js

To set up a scene in three.js, you need a scene, camera, renderer and controls object. They pretty much mean their name in English.

var scene, camera, renderer, controls;
var canvas = document.getElementById('canvas');

// particles set up
var particleCount = 100;
var particles = [];

init();
animate();

Initialize the Scene

The init() function sets up the scene. Let me explain a bit what these things mean.

  1. A Scene object lets you add the objects in the, scene.
  2. Instantiate a PerspectiveCamera, this is a common camera to mimic a 3D view. There are two cameras three.js provides, PerspectiveCamera and OrthographicCamera, the latter is a camera used to mimic a 2D view in a 3D scene.
  3. Instantiating a WebGLRenderer, as opposed to CanvasRenderer, which is usually a fallback for browsers not supporting three.js.
  4. Instantiating an OrbitControls. This is the most interesting class since it determines how the scene interact with you. It’s actually not included in the core three.min.js file and rather is in one of the examples. This particular class mimic a orbit control traditionally found in CAD software where you feel like you’re turning the object in the center. Controls use event listeners in the browser to achieve the interaction between you and the scene. There are some other really interesting ones like DeviceOrientation, if you look at the scene with you phone, it detects the device orientation, mimicking a VR effect.

I didn’t add lights in the scene, trying to keep it simple.

function init() {
  scene = new THREE.Scene();

  camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
  camera.position.z = 1000;

  renderer = new THREE.WebGLRenderer();
  renderer.setClearColor( 0x31AED1, 1);
  renderer.setSize( window.innerWidth, window.innerHeight );

  controls = new THREE.OrbitControls( camera, renderer.domElement );
  controls.enableDamping = true;
  controls.dampingFactor = 0.25;
  controls.enableZoom = false;

    // fillScene();

  canvas.appendChild( renderer.domElement );
  renderer.render( scene, camera );
}

After adding this function, your scene should be set up with a blue background color.

Fill the Scene with Objects

In a 3D scene, an object is made out of its geometry (how it’s shaped), and material (what the color is), these two things creates a mesh. Here I’m just using SphereGeometry and MeshBasicMaterial, which do not need lights to be visible.

I then created 100 particles, put them in random positions and give them directions. The particles are only moving in the x and y direction.

function fillScene() {

    var particleGeometry = new THREE.SphereGeometry(10, 32, 32); // size, number of polys to form this circle
    var particleMaterial = new THREE.MeshBasicMaterial({
        color: 0xFFFFFF,
        transparent: true,
        opacity: 0.5
    });

    // create a random set of particles
    for (var i = 0; i < particleCount; i++) {

        particles[i] = new THREE.Mesh( particleGeometry, particleMaterial );

        //randomize positions
        particles[i].position.x = Math.random() * window.innerWidth * 2 - window.innerWidth;;
        particles[i].position.y = Math.random() * window.innerHeight * 2 - window.innerHeight;
        particles[i].position.z = Math.random() * window.innerWidth * 2 - window.innerWidth;

        particles[i]. direction = {
            x: Math.random(),
            y: Math.random()
        }

        scene.add(particles[i]);
    }
}

Uncomment out the fillScene() function in init(). After this, you should have 100 particles in space. Wait, they aren’t moving? On to the next step.

Animate the Scene

A computer generated image needs to be 60 fps in order to appear smooth. This value is even higher in VR, which is 100 fps.

With requestAnimationFrame(), the browser lines up whatever you pass in (in this case animate()) the function, and calls it every time the browser refreshes. This is more efficient than setTimeOut(). There are some great articles online that explains why so. Now that requestAnimationFrame() is supported across all browsers there is no reason for looking back.

Each time a requestAnimationFrame() is called, we run a for loop that moves the particles a little bit, and bounce them back from an invisible box with the height and width of window.innerWidth.

In the end, don’t forget render the new scene.

function animate() {
        requestAnimationFrame( animate );
        controls.update();

        for (var i = 0; i < particleCount; i++) {
            particles[i].position.x += particles[i].direction.x;
            particles[i].position.y += particles[i].direction.y;

            // if edge is reached, bounce back
            if (particles[i].position.x < -window.innerWidth ||
            particles[i].position.x > window.innerWidth) {
                particles[i].direction.x = -particles[i].direction.x;
            }
            if (particles[i].position.y < -window.innerHeight ||
            particles[i].position.y > window.innerHeight) {
                particles[i].direction.y = -particles[i].direction.y;
            }
        }

        renderer.render( scene, camera );
}

Now you have a set of moving particles in space!
See the complete set of source code here.

VR

After this demo, I changed up a couple lines in the init() function. I un-fixed the camera, and changed the controls. Remember to reference a different JavaScript file called DeviceOrientationControls.js(get the code) in index.html.

camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1100 );
controls = new THREE.DeviceOrientationControls( camera );

If you’re reading this post with a mobile device/tablet, you can see the demo here.

Finally

This is just an ice cube on top of an iceberg that is 3D programming on the web. There are a ton of open source code available for you to explore and change around. I hope this tutorial gives a very basic starting point to where you can begin. In the end. Happy coding!

Leave a Reply

Your email address will not be published.