Create a pulse effect with Three.js, Tween.js, and Vue 2

Three.js is a popular JavaScript library used for creating 3D graphics in web applications. It's a powerful tool that allows you to create stunning visual effects and animations, and it's also relatively easy to use. Tween.js is another JavaScript library that's often used in conjunction with Three.js to create smooth animations.

In this tutorial, we'll be using Three.js, Tween.js, and Vue to create a pulse effect. The pulse effect is a simple but effective animation that makes an object appear as if it's pulsating. It is expected that you know how to setup a Vue environment. Once you've done that, then we'll create a basic Three.js scene with a cylinder on the center of the screen. After that, we'll add the pulse effect to the cube using Tween.js. By the end of this tutorial, you'll have a good understanding of how to use Three.js and Tween.js to create smooth and visually appealing animations in your web applications.

If you prefer to jump straight to the good stuff. Here is a working code sample of what I cover in this article.

https://codesandbox.io/s/three-js-pulsating-example-nknztl?from-embed

Creating the scene

The first step is to pull down your dependancies. You'll need to pull down three.js and tween.js which you can do with the following command.
npm i three
npm i @tweenjs/tween.js
Now that we have our dependancies we can create the scene. First I'll provide the code then we will discuss what's going on.
createScene() {
  this.scene = new THREE.Scene();
  this.camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
  );

  this.camera.position.z = 5;

  this.renderer = new THREE.WebGLRenderer({ antialias: true });
  this.renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(this.renderer.domElement);

  this.renderer.setAnimationLoop(() => {
    this.renderer.render(this.scene, this.camera);
    window.TWEEN.update();
  });
},
  • First we need to assign the scene to a property that we can access later. We will need to add cubes to the scene. 
  • Next, we create the camera perspective, without this the object won't display on the screen. We also set the z axis to 5, with the defaults it wasn't showing. You may need to adjust this to get the object to show correctly.
  • Then we create our WebGLRenderer and set antialias to true as this will get rid of any jagged edges. We also set the size of the renderer to the size of the window width and height. Again, you may need to set this differently based on your needs. Then we append it to the body of the Dom element.
  • And lastly, we create the animation loop.

Creating our geometry

Now that we got the scene our of the way, let's create our geometry that we want displayed on the screen. Our first shape is going to be a cylinder. Here's the code for that then we will discuss.
cylinderGeometry() {
  const geometry = new THREE.CylinderGeometry(0.5, 0.5, 0.5);
  const material = new THREE.MeshBasicMaterial({
    color: 0xdc2626,
    opacity: 0.5,
    transparent: true,
  });
  const cube = new THREE.Mesh(geometry, material);
  cube.rotation.x = Math.PI / 2;

  return cube;
},
  • First thing we need to do is create our CylinderGeometry via THREE and assign it to a variable.
  • Next, we need to create the material. In this case I'm using a basic material but Three.js provides a lot of different options here. I've also set the color, opacity, and transparent property to give it the look I want. You may need to adjust these for your situation.
  • Then we need create the Mesh, which takes the geometry and material as options.
  • And finally, we need to rotate the x axis so that the top of the cylinder is facing us on the screen. It will look like a red circle.
Note: You won't see a circle on your screen just yet, we're going to create our ring geometry and then we will get to adding the objects to your scene.

Now let's create the ring geometry.
ringGeometry() {
  const ringGeometry = new THREE.RingGeometry(0.4, 0.9, 32);
  const ringMaterial = new THREE.MeshBasicMaterial({
    color: 0xdc2626,
    side: THREE.DoubleSide,
    opacity: 0.5,
    transparent: true,
  });
  const ring = new THREE.Mesh(ringGeometry, ringMaterial);
  ring.scale.set(0.5, 0.5, 0.5);

  return ring;
},
  • Creating the ring is almost identical to creating the cylinder. We use a RingGeometry here instead and set the inner radius to 0.4 and the outer radius to 0.9 and the number of thetaSegments to 32 (this is what makes it round).
  • We also set the scale to 0.5. This is important as we're going to use TWEEN to increase the scale to give it the pulsating effect.
And here is what your ring should look like.
pulse-effect-image-two.png 1.07 KB
Before we move on to the pulsating effect, we need to add the geometry to the scene so that we can see it on the screen. To do that we will want to add the following to our mounted() method.
this.cylinder = this.cylinderGeometry();
this.ring = this.ringGeometry();

this.scene.add(this.cylinder, this.ring);
If all has gone well you should now see a red circle on your screen. You won't see the ring you created because we set the scale to .5 which is small then the cylinder. That's ok though, you'll see once we add the pulsating effect why that is.

Creating the pulse effect

Adding the pulse effect is really simple. Here's the code and then we will discuss.
createPulsatingEffect() {
  // This changes the scale of the ring and creates the expanding pulse.
  new window.TWEEN.Tween(this.ring.scale)
    .to(new THREE.Vector3(1, 1, 1))
    .easing(window.TWEEN.Easing.Quadratic.Out)
    .repeat(Infinity)
    .start();

  // This changes the opacity which makes it show and fade away.
  new window.TWEEN.Tween(this.ring.material)
    .to({ opacity: 0 })
    .easing(window.TWEEN.Easing.Quadratic.Out)
    .repeat(Infinity)
    .start();
},
  • First we want to create an animation on the ring that changes the scale. This will make the ring grow bigger, which is why we set the initial scale to .5 and TWEEN will make it grow to a scale of 1. So because the goal is to adjust the scale that is the first argument we will supply to the tween.
  • The to() method is how big we want the scale to go, so we set it to 1 here.
  • The easing() method gives it a smooth animation.
  • The repeat() method allows the animation to repeat. You can provide a number, so for instance if you want it to repeat 5 times you can put 5 or you can put Infinity to repeat forever.
  • Then we call the start() method.
  • The first TWEEN targets the scale and the second TWEEN targets the opacity which makes the ring show and fade out.

Note: if you save at this point and try and run the animation it won't work. You have to add the TWEEN.update() method to the animation loop in order for it to work. Like this:

this.renderer.setAnimationLoop(() => {
  this.renderer.render(this.scene, this.camera);
  window.TWEEN.update();
});

Conclusion

In this tutorial, we've learned how to create a pulse effect using Three.js, Tween.js, and Vue. We started by creating a basic Three.js scene with a cylinder and a ring. Then, we added the pulse effect to the ring geometry using Tween.js, which allowed us to smoothly animate the cube's scale and opacity over time. We also explored some of the features and settings of Three.js, including creating a scene, camera, and renderer, as well as adding geometry and materials to create 3D objects. By the end of this tutorial, you should have a good understanding of how to use Three.js and Tween.js to create smooth and visually appealing animations in your web applications.