Skip to content

Three.js - Drawing Lines

After exploring various materials in Three.js, let’s dive into materials specifically designed for drawing lines. Lines are fundamental for creating wireframes, outlines, paths, and various geometric patterns in your 3D scenes.

The core of drawing a line in Three.js involves two main components: a geometry defining the path of the line, and a material defining its appearance.

THREE.BufferGeometry is the modern, high-performance way to define any custom shape in Three.js. To create a line, you provide it with a series of points, or vertices, that the line will connect in sequence.

import * as THREE from 'three';
// 1. Define the points (vertices) of the line
const points = [];
points.push(new THREE.Vector3(-10, 0, 0)); // Start point
points.push(new THREE.Vector3(0, 10, 0)); // Middle point
points.push(new THREE.Vector3(10, 0, 0)); // End point
// 2. Create a BufferGeometry from the points
const geometry = new THREE.BufferGeometry().setFromPoints(points);

Here, THREE.Vector3(x, y, z) creates a point in 3D space. We create an array of these vectors and then use the .setFromPoints() method to efficiently construct our geometry.

Note: THREE.Line draws segments between each consecutive pair of vertices. It does not automatically connect the last vertex to the first to close the loop. For a closed line, you must add the first point again at the end of the array.

Three.js offers two primary materials for lines:

// For a solid, continuous line
const material = new THREE.LineBasicMaterial({ color: 0x00ff00 });
// For a dashed line
const dashedMaterial = new THREE.LineDashedMaterial({
color: 0xffea00,
linewidth: 1, // Note: this is largely ignored, see below
scale: 1,
dashSize: 3, // Length of a dash
gapSize: 1, // Length of the gap between dashes
});

Once you have your geometry and material, you combine them using THREE.Line:

const line = new THREE.Line(geometry, material);
scene.add(line);
// For dashed lines, you MUST compute line distances
const dashedLine = new THREE.Line(geometry, dashedMaterial);
dashedLine.computeLineDistances();
scene.add(dashedLine);

A frequent point of confusion is the linewidth property on LineBasicMaterial and LineDashedMaterial. Due to limitations in how modern GPUs render lines (specifically in the underlying WebGL API), setting linewidth to a value greater than 1 will not have an effect on most platforms.

If you need to render lines with a controllable, thick width, you must use more advanced techniques. A popular solution is to use Line2 from the Three.js examples, which builds the line from a thin mesh. This approach offers more visual control at the cost of slightly more complex setup and performance.

The following example demonstrates creating a basic line and uses the modern lil-gui library to allow real-time manipulation of its vertices. This showcases a more efficient way to update geometries compared to recreating them on every change.

This example assumes a modern JavaScript development environment. You can set up a project quickly using a tool like Vite.

  1. Initialize your project: npm create vite@latest my-threejs-app -- --template vanilla
  2. Install dependencies: cd my-threejs-app and then npm install three lil-gui
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import GUI from 'lil-gui';
/**
* Base Setup
*/
// GUI
const gui = new GUI();
// Scene
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a1a1a);
// Camera
const sizes = {
width: window.innerWidth,
height: window.innerHeight
};
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 0.1, 100);
camera.position.set(0, 5, 20);
scene.add(camera);
// Renderer
const renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(sizes.width, sizes.height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
// Controls
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
/**
* Line Drawing
*/
// 1. Define Points
const points = [];
points.push(new THREE.Vector3(-10, 0, 0));
points.push(new THREE.Vector3(0, 10, -5));
points.push(new THREE.Vector3(10, 0, 0));
// 2. Create Geometry
const geometry = new THREE.BufferGeometry().setFromPoints(points);
// 3. Create Material
const material = new THREE.LineBasicMaterial({ color: 0x00ff00 });
// 4. Create Line
const line = new THREE.Line(geometry, material);
scene.add(line);
/**
* GUI for real-time updates
*/
function updateLine() {
// More performant: update the existing geometry's positions
line.geometry.setFromPoints(points);
}
points.forEach((point, i) => {
const folder = gui.addFolder(`Point ${i + 1}`);
folder.add(point, 'x', -15, 15, 0.1).name('X').onChange(updateLine);
folder.add(point, 'y', -15, 15, 0.1).name('Y').onChange(updateLine);
folder.add(point, 'z', -15, 15, 0.1).name('Z').onChange(updateLine);
folder.open();
});
/**
* Responsiveness
*/
window.addEventListener('resize', () => {
sizes.width = window.innerWidth;
sizes.height = window.innerHeight;
camera.aspect = sizes.width / sizes.height;
camera.updateProjectionMatrix();
renderer.setSize(sizes.width, sizes.height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
});
/**
* Animation Loop
*/
const tick = () => {
controls.update();
renderer.render(scene, camera);
window.requestAnimationFrame(tick);
};
tick();

To create a dashed line, simply swap the material and remember to call .computeLineDistances() on the Line object. This extra step is necessary for Three.js to calculate how to apply the dashes and gaps correctly along the line’s length.

// ... (setup is the same as above)
// Create a Dashed Material
const dashedMaterial = new THREE.LineDashedMaterial({
color: 0xffea00,
dashSize: 1,
gapSize: 0.5
});
// Create the Line object
const dashedLine = new THREE.Line(geometry, dashedMaterial);
// CRITICAL STEP for dashed lines
dashedLine.computeLineDistances();
scene.add(dashedLine);

Drawing lines is a versatile skill. You can use it to:

  • Visualize data: Create line graphs in 3D space.
  • Outline objects: Draw a wireframe of a complex model for artistic or debugging purposes.
  • Create helpers: Draw grids, axes, or other guides in your scene.
  • Illustrate paths: Show the trajectory of an object in an animation.

Experiment by adding more points, creating closed loops, or even animating the points’ positions over time within the tick function for dynamic effects.