News

Code animated fireworks with three.js

ThreeJSfireworks

Build a simple scene, add textures to materials, create lights and blow things up. What more could you want

ThreeJSfireworks

GET THE CODE FOR THIS TUTORIAL

WebGL is one of the best things about the modern web. We, as developers, looked at 3D modelling, shading, rendering and all the other things that come with the realm of the third dimension. The use of 3D graphics in the browser enables us to make the best of real-time graphics generation with just pure JavaScript. Rendering anything in 3D is a far more complex process than drawing a square on a <canvas> element, but with complexity comes difficulty in implementation.

Fortunately, three.js is here to help us. It’s a JavaScript library that helps us write WebGL content without having to worry about a great deal of maths or 3D rendering computer science (well, not too much). Think of three.js as a kind of jQuery for WebGL, everything is simpler and follows patterns. There’s no more fiddling about with polyfills or trying to get things to light consistently. Get the full code for this tutorial at filesilo.co.uk/webdesigner.


1. Grab the resources

Three.js has a ton of helper libraries and other code bits to help it get on with its work. We’re going to use the core three.js library and the OrbitControls.js library to handle our camera movements. Download the project files from FileSilo and have a look around the scripts folder.

2. Set up the renderer

The meat of our program is in scripts/fireworks.js. In the init function on lines 181 – 199 we create a renderer, which is where our 3D scene will be drawn to. We then create and position a camera to see with and point it at 0,0,0 of our scene. Whatever happens in front of our ‘camera’ is what will be shown on our rendering element.

renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
camera.position.x = 2;
camera.position.y = 43;
camera.position.z = 35;
camera.lookAt( new THREE.Vector3(0,0,0));

3. Set the scene

Next we call createScene(). Here, we create the ground for our scene and some light (which we call moonlight) to light up our scene. Without light in our scene we won’t be able to see anything. The ground and the moonlight are added to our scene with ‘scene.add(OBJECT);’.

var groundTexture = new THREE.ImageUtils.loadTexture( '/assets/images/ground.jpg' ),
groundGeometry = new THREE.PlaneBufferGeometry( 150, 150, 32 ),
groundMaterial = new THREE.MeshPhongMaterial( {side: THREE.DoubleSide, map: groundTexture} );
ground = new THREE.Mesh( groundGeometry, groundMaterial );
moonlight = new THREE.PointLight( 0xffffff, 1, 100 );
moonlight.position.set(0, 10, 5);
scene.add( ground );
scene.add( moonlight );

4. Rendering

Now that we have some things in our scene that we want to see, we can tell three.js to render them. We use requestAnimationFrame() to call our render function which will draw our scene as close to 60FPS as your computer can handle with ‘renderer.render( scene, camera );’. Right now, you’ll only see illuminated turf.

renderer.render( scene, camera );
requestAnimationFrame( render );

5. Set off a firework

If you press Space, a colourful firework will set off into the sky and then detonate. In addEvents(), we push a new firework() to our fireworks array and once there, our render function will work through and draw every firework we’ve set off.

window.addEventListener('keydown', function(e){
if(e.keyCode === 32){
// (Radius, Width, Height, Color)
fireworks.push(new firework( 0.2,32,32, new THREE.Color( colors[Math.floor(Math.random() * colors.length)] ) ));
ground.material._needsUpdate = true;
} }, false);

6. Detonation

What is a firework anyway? Well, in this context, it’s an object that will keep track of the position, velocity, sparks and light sources of our firework as it travels to its beautiful, but ultimately tragic demise. Once our firework reaches a certain height above our ground, it will explode and animate our explosion with the detonate() function on lines 60 – 82.

7. Light ‘em up

Our fireworks need to make light that shines on other things around it. The material that makes up our fireworks in flight can’t (easily) do this on its own, so instead, we create a new light the same colour as the firework in the same place. Do this with ‘this.light = new THREE.PointLight( color, 10, 4 );’, which you may notice is the exact same way we created moonlight.

// Line 29
this.light = new THREE.PointLight( color, 10, 4 );
// Lines 96 – 98
f.light.position.x = f.object.position.x;
f.light.position.y = f.object.position.y;
f.light.position.z = f.object.position.z;

8. Particles and CPU cycles

When our firework explodes, we want to see colourful sparks, but do we really want to render dozens of new objects to do so? No, we don’t. Instead, we can create a point cloud, which is a fancy way of saying a particle system. This is basically one big object, but made up of loads of little bits with space in-between, it’s much friendlier for our graphics card.

9. The sparks

On lines 38 – 44, we create a point for each spark that we want to have and give each a random velocity. On lines 47 – 53 we tell three.js what size and colour we want our particles to be and then we add them to the point cloud on line 55. As a cheat, we add a JPG to each of our sparks to fine-tune how each point should look. On lines 100 – 110, we check how high our firework is, whether or not it should explode and whether or not we should be animating it if the explosion has started.

var sparks = new THREE.Geometry();
for (var i = 0; i < Math.random() * 1000 | 0; i ++ ) {
var spark = new THREE.Vector3(0,0,0);
spark.velocity = [ Math.random() – Math.random(), Math.random() – Math.random(), Math.random() – Math.random()];
sparks.vertices.push( spark );
}
this.sparkMaterial = new THREE.PointCloudMaterial( {
size: 1.5,
map: THREE.ImageUtils.loadTexture("assets/images/spark.jpg"),
blending: THREE.AdditiveBlending,
transparent: true,
color : color
});
this.particles = new THREE.PointCloud( sparks, this.sparkMaterial );

10. Remove particles

A firework doesn’t last all night. So once it’s petered out, it’s probably best to forget about it, otherwise it’s just going to clog up our computer’s memory. If we pass through our firework and its lights to the removeObjectFromScene() function on lines 19 – 22, three.js will remove our asploded fireworks from the scene and GC will take care of the rest.

// Lines 106 – 110
} else if(f.hasDetonated && f.explosionLight.distance <= 1){
removeObjectFromScene(f.explosionLight);
removeObjectFromScene(f.particles);
fireworks.splice(aa, 1);
}
×