Three.js Lines — Drawing Lines, Polylines, and Line Segments
In this tutorial, you will learn about Three.js Lines. We cover key concepts, practical examples, and best practices to help you master this topic.
Three.js lines draw connected points in 3D space using BufferGeometry and LineBasicMaterial, enabling wireframes, paths, grids, and vector visualizations.
What You'll Learn
By the end of this guide, you will create lines from arrays of points, draw polylines with dashed patterns, render individual line segments, create grid helpers, and build interactive path editors.
Why Lines Matter
Lines visualize paths, boundaries, and abstract data. In Durga Antivirus Pro, lines show network traffic routes between nodes, attack paths traced by malware, and perimeter boundaries. Without lines, 3D scenes lack wireframes, guides, and data connections.
flowchart LR
A[Point Array] --> B[BufferGeometry]
B --> C[setFromPoints]
C --> D[Line]
E[LineBasicMaterial] --> D
F[Dash Settings] --> G[LineDashedMaterial]
G --> D
D --> H[Rendered Path]
Basic Line
var points = [
new THREE.Vector3(-3, 0, 0),
new THREE.Vector3(-1, 1, 0),
new THREE.Vector3(1, -1, 0),
new THREE.Vector3(3, 0, 0)
];
var geometry = new THREE.BufferGeometry().setFromPoints(points);
var material = new THREE.LineBasicMaterial({ color: 0x4488ff });
var line = new THREE.Line(geometry, material);
scene.add(line);
Expected output: A blue zigzag line connecting the four points in order.
Dashed Lines
var material = new THREE.LineDashedMaterial({
color: 0xff6600,
dashSize: 0.3,
gapSize: 0.15,
linewidth: 2
});
var line = new THREE.Line(geometry, material);
line.computeLineDistances();
scene.add(line);
Expected output: An orange dashed line with 0.3-unit dashes and 0.15-unit gaps.
Why computeLineDistances: Dashed material needs per-vertex distance data along the line. Without calling this, dashes render incorrectly.
Line Segments (Unconnected)
var segmentPoints = [
new THREE.Vector3(-3, 1, 0),
new THREE.Vector3(-2, -1, 0),
new THREE.Vector3(0, 1, 0),
new THREE.Vector3(1, -1, 0),
new THREE.Vector3(3, 1, 0),
new THREE.Vector3(4, -1, 0)
];
var geometry = new THREE.BufferGeometry().setFromPoints(segmentPoints);
var material = new THREE.LineBasicMaterial({ color: 0x44ff88 });
var segments = new THREE.LineSegments(geometry, material);
scene.add(segments);
Expected output: Three unconnected line segments. Each pair of consecutive points forms a separate segment.
Grid Helper
var gridHelper = new THREE.GridHelper(20, 20, 0x8888ff, 0x444466);
scene.add(gridHelper);
var axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
Expected output: A 20x20 grid on the XZ plane with blue major lines and darker minor lines, plus RGB axes at the origin.
Animated Line Drawing
var totalPoints = 100;
var positions = new Float32Array(totalPoints * 3);
var geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setDrawRange(0, 0);
var material = new THREE.LineBasicMaterial({ color: 0x00ffcc });
var line = new THREE.Line(geometry, material);
scene.add(line);
var drawCount = 0;
function animate() {
if (drawCount < totalPoints) {
var i = drawCount;
var t = i / totalPoints;
positions[i * 3] = Math.cos(t * Math.PI * 6) * 3;
positions[i * 3 + 1] = Math.sin(t * Math.PI * 8) * 2;
positions[i * 3 + 2] = t * 4 - 2;
geometry.attributes.position.needsUpdate = true;
geometry.setDrawRange(0, drawCount + 1);
drawCount++;
}
}
Expected output: A helix-like curve that draws itself progressively point by point.
Common Mistakes
1. Using Mesh Instead of Line
Lines and meshes use different geometry expectations. Line geometry is an array of connected vertices, not triangles.
2. Forgetting computeLineDistances for Dashed Lines
Dashed lines render with incorrect dash patterns if computeLineDistances is not called after geometry creation.
3. Line Not Visible
LineBasicMaterial does not respond to lights. Ensure the line color has sufficient contrast against the background.
4. Too Many Points in a Single Line
Lines with millions of points impact performance. Use LineSegments for disconnected segments and simplify paths where possible.
5. Ignoring Line Width Limitations
WebGL line width is limited to 1 on most browsers and GPUs. Use thicker geometry (TubeGeometry) for wide lines.
Practice Questions
Q1: What is the difference between Line and LineSegments? A: Line connects all points as a single continuous polyline. LineSegments treats each consecutive pair as an independent segment.
Q2: Why do dashed lines need computeLineDistances? A: The dash material uses accumulated distance along the line to position dashes correctly. This function precomputes that data.
Q3: How do you animate a line drawing progressively? A: Use setDrawRange on the geometry to control how many vertices are rendered, incrementally increasing the count.
Q4: What is the GridHelper class? A: A built-in helper that creates a coordinate grid on the XZ plane with configurable size, divisions, and colors.
Q5: Why is line width limited in WebGL? A: WebGL implementations vary, but most only guarantee line width of 1 pixel. Use TubeGeometry for thick lines.
Challenge: Build a path drawing tool. Click in 3D space to add points. The line updates in real time showing the path. Add undo, clear, and export the points as JSON.
FAQ
Try It Yourself — Line Drawing App
Build an interactive page where clicking in 3D space adds points connected by a line. Includes dashed mode toggle and grid helper.
<!DOCTYPE html>
<html>
<head>
<title>3D Line Drawer</title>
<style>
body { margin: 0; overflow: hidden; font-family: sans-serif; }
#ui { position: absolute; top: 10px; left: 10px; background: rgba(0,0,0,0.8); color: white; padding: 15px; border-radius: 8px; }
#ui button { padding: 6px 12px; margin: 4px; cursor: pointer; }
#pointInfo { margin-top: 8px; font-size: 12px; color: #aaa; }
</style>
</head>
<body>
<div id="ui">
<button onclick="clearPoints()">Clear</button>
<button onclick="toggleDashed()">Toggle Dashed</button>
<div id="pointInfo">Click on the ground to add points</div>
</div>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.170.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.170.0/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
var scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a1a2e);
var camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 100);
camera.position.set(6, 6, 8);
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
renderer.setPixelRatio(devicePixelRatio);
document.body.appendChild(renderer.domElement);
var controls = new OrbitControls(camera, renderer.domElement);
var grid = new THREE.GridHelper(12, 12, 0x8888ff, 0x444466);
scene.add(grid);
var points = [];
var lineMaterial = new THREE.LineBasicMaterial({ color: 0x44ff88 });
var lineGeometry = new THREE.BufferGeometry();
var line = new THREE.Line(lineGeometry, lineMaterial);
scene.add(line);
var dashed = false;
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
window.addEventListener('click', function(event) {
mouse.x = (event.clientX / innerWidth) * 2 - 1;
mouse.y = -(event.clientY / innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
var plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
var intersect = new THREE.Vector3();
raycaster.ray.intersectPlane(plane, intersect);
if (intersect) {
points.push(intersect.clone());
var sphere = new THREE.Mesh(
new THREE.SphereGeometry(0.1, 16, 16),
new THREE.MeshBasicMaterial({ color: 0xff6600 })
);
sphere.position.copy(intersect);
scene.add(sphere);
updateLine();
document.getElementById('pointInfo').textContent = 'Points: ' + points.length;
}
});
function updateLine() {
var geo = new THREE.BufferGeometry().setFromPoints(points);
line.geometry.dispose();
line.geometry = geo;
if (dashed) {
line.material = new THREE.LineDashedMaterial({
color: 0x44ff88, dashSize: 0.2, gapSize: 0.1
});
line.computeLineDistances();
} else {
line.material = new THREE.LineBasicMaterial({ color: 0x44ff88 });
}
}
window.clearPoints = function() {
points = [];
var spheres = [];
scene.traverse(function(child) {
if (child.isMesh && child.geometry.type === 'SphereGeometry') spheres.push(child);
});
spheres.forEach(function(s) { scene.remove(s); s.geometry.dispose(); s.material.dispose(); });
line.geometry.dispose();
line.geometry = new THREE.BufferGeometry().setFromPoints([]);
document.getElementById('pointInfo').textContent = 'Points: 0';
};
window.toggleDashed = function() {
dashed = !dashed;
updateLine();
};
function animate() {
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
window.addEventListener("resize", function() {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
});
</script>
</body>
</html>
What's Next
Create skeletal animations with bones and skinning.
Bones Skinning — Skeletal animation in Three.js. Project — Three.js project tutorial applying all concepts.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro