00:00/00:00
3:22
00:03:22

Shortcuts ⌨️

  • SPACE to play / pause
  • ARROW RIGHT or L to go forward
  • ARROW LEFT or J to go backward
  • ARROW UP to increase volume
  • ARROW DOWN to decrease volume
  • F to toggle fullscreen
  • M to toggle mute
  • 0 to 9 to go to the corresponding part of the video
  • SHIFT + , to decrease playback speed
  • SHIFT + . or ; to increase playback speed

Unlock content 🔓

To get access to 91 hours of video, a members-only Discord server, subtitles, lesson resources, future updates and much more join us for only $95!

Want to learn more? 👍

45%

That's the end of the free part 😔

To get access to 91 hours of video, a members-only Discord server and future updates, join us for only $95!

Next lesson
34.

Fireworks

Difficulty Hard

Introduction 00:00

In this lesson we are going to create fireworks whenever the user clicks on the screen:

The trick here is that we are going to use particles and animate them in a single vertex shader.

  • The particles start to expand fast in every direction
  • They scale up even faster
  • They start to fall down slowly
  • They scale down
  • They twinkle as they disappear

That doesn’t look too complicated, now does it? Remember that we need to do that in the vertex shader in order to achieve good performance, meaning that the same code will be executed for all particles.

Also, all the previously mentioned animation steps are intertwined, and we need to find a way to handle the various steps while combining them.

At the very end, we are going to implement a sky background as a bonus.

Setup 02:09

We get the usual simple setup with a few things to note:

  • One test cube to make sure that the experience is running properly
  • An instance of lil-gui that we are going to use for the sky
  • The vite-plugin-glsl dependency to handle GLSL files
  • OrbitControls to rotate around
  • TextureLoader that is ready to load some fancy particle textures
  • gsap in the dependencies that we are going to use to animate one single property

Base particles 02:57

Let’s create the particles instead of that cube.

Remove the whole Test section with the cube and create a Fireworks section instead, right before the Animate section:

/**
 * Fireworks
 */

We are going to have a black screen for a few minutes.

Since we need to create a new firework on each click, we better set a function for it.

Create a createFirework function and call it immediately:

const createFirework = () =>
{

}

createFirework()

We are going to add various parameters as we go such as the position, the radius, the amount of particles, etc.

Most of the following code will be inside createFirework.

Geometry

We are going to instantiate the usual BufferGeometry and fill it with random positions, but first, we need to know how many particles we want.

Add a count parameter to the createFirework function and set it to 100 when calling the function:

const createFirework = (count) =>
{

}

createFirework(100)

Don’t worry, we are going to add a lot more soon.

Now create a positionsArray using a Float32Array with a length of count * 3 since we need 3 values per vertex (xyz):

const createFirework = (count) =>
{
    // Geometry
    const positionsArray = new Float32Array(count * 3)
}

Loop from 0 to count, create an i3 which is i * 3 so that we have a value going 3 by 3 and fill the positionsArray array with random values:

const createFirework = (count) =>
{
    // Geometry
    const positionsArray = new Float32Array(count * 3)

    for(let i = 0; i < count; i++)
    {
        const i3 = i * 3

        positionsArray[i3    ] = Math.random()
        positionsArray[i3 + 1] = Math.random()
        positionsArray[i3 + 2] = Math.random()
    }
}

We are moving swiftly through this because we have done it multiple times before. But to put it in a nutshell: each loop corresponds to one vertex. For now, we’re filling the array with random positions, but later we will send more specific coordinates.

Subtract 0.5 to each Math.random() so that values are as much positive as negative:

const createFirework = (count) =>
{
    // Geometry
    const positionsArray = new Float32Array(count * 3)

    for(let i = 0; i < count; i++)
    {
        const i3 = i * 3

        positionsArray[i3    ] = Math.random() - 0.5
        positionsArray[i3 + 1] = Math.random() - 0.5
        positionsArray[i3 + 2] = Math.random() - 0.5
    }
}

We can now create the actual BufferGeometry:

const createFirework = (count) =>
{
    // ...

    const geometry = new THREE.BufferGeometry()
}

Then, add a 'position' attribute to it using a Float32BufferAttribute:

const createFirework = (count) =>
{
    // ...

    const geometry = new THREE.BufferGeometry()
    geometry.setAttribute('position', new THREE.Float32BufferAttribute(positionsArray, 3))
}

3 corresponds to the itemSize and indicates that we have 3 values per vertex (because xyz).

Material

For the material, let’s not rush anything and start with a PointsMaterial, which we will later replace with a ShaderMaterial:

const createFirework = (count) =>
{
    // ...

    // Material
    const material = new THREE.PointsMaterial()
}

Points

We can finally create the particles that we are going to name firework (singular) and use a Points instance, then add() it to the scene:

const createFirework = (count) =>
{
    // ...

    // Points
    const firework = new THREE.Points(geometry, material)
    scene.add(firework)
}

Finally, we’re getting something on screen, and we have a long and exciting road ahead.

Want to learn more?

That's the end of the free part 😔

To get access to 91 hours of video, a members-only Discord server and future updates, join us for only $95!

How to use it 🤔

  • Download the Starter pack or Final project
  • Unzip it
  • Open your terminal and go to the unzip folder
  • Run npm install to install dependencies
    (if your terminal warns you about vulnerabilities, ignore it)
  • Run npm run dev to launch the local server
    (project should open on your default browser automatically)
  • Start coding
  • The JS is located in src/script.js
  • The HTML is located in src/index.html
  • The CSS is located in src/style.css

If you get stuck and need help, join the members-only Discord server:

Discord