Animating Lines and Curves in Three.js with MeshLine

Published on 13 Feb, 2025 | ~5 min read | Demo

In this short read, we'll explore a simple method for animating lines in Three.js and use it to create an animated heart as the demo. (Fitting, right? Hint: check the date!)

MeshLine: The Basics

First and foremost, create a new project and install Three.js, or just use my Three.js boilerplate.

As mentioned in the title, we won't use the classic combination of LineBasicMaterial and one of the curve creation classes. Instead, we'll use a third-party module called MeshLine.

With that said, let's install it by running this command in the terminal: npm install meshline

Next, of course, we'll import it into our project.

import { MeshLineGeometry, MeshLineMaterial } from 'meshline';

As you can see, we have a new special type of material and geometry for the lines.

As usual, to add something to the scene, we need to create a geometry and a material, then combine them into a mesh.

So, let's start with the geometry by creating a new instance of MeshLineGeometry.

const geometry = new MeshLineGeometry();

Next, we'll need to create an array containing the coordinates of the points that form the curve we want to draw in our scene.

const geometryPoints = [
  new THREE.Vector3(-2, 3, 0),
  new THREE.Vector3(3, 3, -2),
  new THREE.Vector3(0, -2, 0),
  new THREE.Vector3(-2, 0, 2),
  new THREE.Vector3(-2, 6, 5),
  new THREE.Vector3(5, 2, -3),
];

Now, we'll assign these coordinates to the geometry using the setPoints() method.

geometry.setPoints(geometryPoints);

And that's all for the geometry.

For the material, we'll create a new instance of the MeshLineMaterial class and pass the color, width, and resolution as properties of the configuration object.

const lineMaterial = new MeshLineMaterial({
  color: new THREE.Color(0xff0000),
  lineWidth: 1,
  resolution: new THREE.Vector2(window.innerWidth, window.innerHeight),
});

With that done, we'll simply create a new mesh, add it to the scene, and that's it!

const mesh = new THREE.Mesh(geometry, lineMaterial);
scene.add(mesh);
Meshline
Meshline

The Animated Heart

MeshLineMaterial comes with a set of interesting properties, but in this article, I'll highlight just three of them that we'll need to achieve the best animation possible.

First, let's create a loop to generate a smooth curve that forms the shape of a heart.

const geometry = new MeshLineGeometry();

// const geometryPoints = [
//   new THREE.Vector3(-2, 3, 0),
//   new THREE.Vector3(3, 3, -2),
//   new THREE.Vector3(0, -2, 0),
//   new THREE.Vector3(-2, 0, 2),
//   new THREE.Vector3(-2, 6, 5),
//   new THREE.Vector3(5, 2, -3),
// ];

const geometryPoints = [];
for (let t = 0; t <= Math.PI * 2; t += 0.01) {
  const x = 16 * Math.pow(Math.sin(t), 3);
  const y =
    13 * Math.cos(t) -
    5 * Math.cos(2 * t) -
    2 * Math.cos(3 * t) -
    Math.cos(4 * t);
  geometryPoints.push(new THREE.Vector3(x, y, 0));
}

geometry.setPoints(geometryPoints);

Next, let's update the camera's position to get a full view of the masterpiece.

// camera.position.set(6, 8, 14);
camera.position.set(0, 0, 50);
Heart shape generated using the generateHeartCoordinates() function
Heart shape generated using the generateHeartCoordinates() function

"Dude, you're a genius! How did you figure out the math behind this perfectly shaped heart?!"

ChatGPT is really helpful when it comes to such tasks!
ChatGPT is really helpful when it comes to such tasks!

The first property we'll need for our animation is dashArray, which, as the name suggests, transforms continuous lines into dashed ones.

const lineMaterial = new MeshLineMaterial({
  color: new THREE.Color(0xff0000), // Line color (red in this case)
  lineWidth: 1,
  resolution: new THREE.Vector2(window.innerWidth, window.innerHeight),
  dashArray: 0.2,
});
dashArray
dashArray

To animate this, we'll keep updating the dash offset. This technique is commonly used for animating lines in CSS or with libraries like GSAP and Anime.js to animate SVG paths.

function animate() {
  lineMaterial.dashOffset -= 0.003;
  renderer.render(scene, camera);
}

To make the animation look like it's drawn with a single line, increase the dashArray value.

const lineMaterial = new MeshLineMaterial({
  color: new THREE.Color(0xff0000), // Line color (red in this case)
  lineWidth: 1,
  resolution: new THREE.Vector2(window.innerWidth, window.innerHeight),
  // dashArray: 0.2,
  dashArray: 2,
});

Animated demo.

The first problem is that the dashed part isn't completely invisible. Zoom in, and you'll notice it. This makes the line look cut because the section overlapped by the dashed part appears in front.

The dashed part isn't invisible
The dashed part isn't invisible

That being said, to make the dashed part truly invisible, we'll set the transparent property to true.

lineMaterial.transparent = true;
The cut line problem still remains
The cut line problem still remains

As you can see, we resolved one of the two issues by making the dashed part truly invisible. However, the visible part still looks cut because the dash is in front of it.

To prevent this occlusion culling issue, we'll set the depthTest property to false.

lineMaterial.depthTest = false;
No line cut issue
No line cut issue

We can make the curve change its width. For example, it can start thick and gradually become thinner toward the endpoint.

To do that, we'll add an arrow function as the second argument to the setPoints() method.

// geometry.setPoints(geometryPoints);
geometry.setPoints(geometryPoints, (p) => 1 - p);
Thick to thin curve
Thick to thin curve

We can do the opposite, meaning making the curve start thin and end thick.

// geometry.setPoints(geometryPoints);
// geometry.setPoints(geometryPoints, (p) => 1 - p);
geometry.setPoints(geometryPoints, (p) => 1 - (1 - p));
Thin to thick curve
Thin to thick curve

Finally, we'll simply do some post-processing to the scene, and we're all set!

Glowing heart
Glowing heart

Animated demo.

Wrap Up

And there you have it — another cool trick for your Three.js toolset pocket.

Sending you lots of love, and see you next time!

Buy me a coffee

Credits and Resources

Related Content