on webgl, Writings, Tutorials, ThreeJS, Code

Seeing Sounds [Part 1]: Creating a star field

This tutorial assumes you have a basic knowledge of three.js, and know how to set up a very simple scene using the three.js renderer and camera. If you don't, then first have a look at my getting started guide here.

In this part, we're going to get more familiar with three.js by building a star field. The field is essentially an array of moving spheres, so the best place to start is by creating a just single sphere. Once we've created our first star, we can just replicate it to create a whole field of them, followed by adding some movement. Here is the outcome of this part of the Seeing Sounds series:

To get started, all you'll need is a local copy of three.js, which you can get from its GitHub repository, and we'll then code everything else within a single HTML file for simplicity.

Step 1: Create a single sphere

We're going to jump straight into the code now.

Create the HTML project file

The snippet below shows our main HTML project file, with a space for adding our single sphere:

<!doctype html>  
<html lang="en">
<head>
  <title>Starfield Tutorial</title>
  <meta charset="utf-8">
</head>
<body style="margin:0px;">

  <script src="/Stars/three.js"></script>

  <script>

    //Sphere code will go here

  </script>

</body>
</html>

Add the sphere

Between the script tags above (where it says "Sphere code will go here"), add the following code which will set up a basic three.js scene, and render a sphere on it. For more detailed instructions on how this is achieved, follow this tutorial I made earler, which has very similar code for adding a cube rather than a sphere.

Here's what you'll get at the end of this step:

Code to add:
<!doctype html>  
<html lang="en">
<head>
  <title>Starfield Tutorial</title>
  <meta charset="utf-8">
</head>
<body style="margin:0px;">

  <script src="/Stars/three.js"></script>

  <script>

    //Declare three.js variables
    var camera, scene, renderer, sphere;

    //assign three.js objects to each variable
    function init(){

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

        //scene
        scene = new THREE.Scene();

        //renderer
        renderer = new THREE.WebGLRenderer();
        //set the size of the renderer
        renderer.setSize( window.innerWidth, window.innerHeight );

        //add the renderer to the html document body
        document.body.appendChild( renderer.domElement );
    }


    function addSphere(){
        // Create a sphere to make visualization easier.
        var geometry   = new THREE.SphereGeometry(0.5, 32, 32)
        var material = new THREE.MeshBasicMaterial( {color: 0xffffff} );
        sphere = new THREE.Mesh(geometry, material)
        scene.add(sphere)


    }

    function render() {
        //get the frame
        requestAnimationFrame( render );

        //render the scene
        renderer.render( scene, camera );
    }

    init();
    addSphere();
    render();

  </script>

</body>
</html>

Step 2: Create a stars array

Now instead of having just one sphere, in this step we're going to create a load of them to produce this:

If you look at line 14 of the code snippet of the previous step, you'll see we declared a 'sphere' variable. To start with, we need to change this to a 'stars' array for holding many spheres, so line 14 becomes:

//Declare three.js variables  
var camera, scene, renderer, stars=[];

The next bit we need to change is the addSphere function (line 36). Since we already know how to add one sphere, we just need to do this multiple times, and add each one to a stars array - we'll do this inside a for loop:

    function addSphere(){

                // The loop will move from z position of -1000 to z position 1000, adding a random particle at each position.
                for ( var z= -1000; z < 1000; z+=20 ) {

                    // Make a sphere (exactly the same as before).
                    var geometry   = new THREE.SphereGeometry(0.5, 32, 32)
                    var material = new THREE.MeshBasicMaterial( {color: 0xffffff} );
                    var sphere = new THREE.Mesh(geometry, material)

                    // This time we give the sphere random x and y positions between -500 and 500
                    sphere.position.x = Math.random() * 1000 - 500;
                    sphere.position.y = Math.random() * 1000 - 500;

                    // Then set the z position to where it is in the loop (distance of camera)
                    sphere.position.z = z;

                    // scale it up a bit
                    sphere.scale.x = sphere.scale.y = 2;

                    //add the sphere to the scene
                    scene.add( sphere );

                    //finally push it to the stars array
                    stars.push(sphere);
                }
    }

As seen above, the addSphere function has only changed a little from what it was before. We first add a for loop to move the z position of the camera from -1000 to 1000 (line 4). Then, within the loop we use the exact same code as before to create a sphere, but this time we don't add it to the scene straight away (lines 7-9). Instead, a random x and y position is added to the sphere, and it's z position is then set based on where we are in the overall loop (lines 12-16).

Finally, the sphere is scaled up slightly so we can see it better (line 19), and then added to the scene, and to the stars array (lines 22 and 25).

Step 3: Animate the star field

This is the final step, and all done by adding one suprisingly simple function, which we'll call animateStars:

    function animateStars() {

        // loop through each star
        for(var i=0; i<stars.length; i++) {

            star = stars[i];

            // move it forward by a 10th of its array position each time
            star.position.z +=  i/10;

            // once the star is too close, reset its z position
            if(star.position.z>1000) star.position.z-=2000;
        }
    }

Here, we are iterating through every star in the stars array (line 4), and simply incrementing the z position (distance from the camera) of the star (line 9). Once the star is too close to the camera, we simply reset its z position so it starts moving forward again from the back (line 12).

Well done

At this point, you should have created the star field as shown in the demo at the top of this tutorial.

The final source code is available here on Github.

The next tutorial in this series will look into creating the terrain at the bottom of the visualization.