The three.js JavaScript library does not provide direct support for gradient colored lines. In this Part-2 of my Groot Clock articles I describe how I created the custom gradient lines and circle-lines used in the project.

As in the Part-1 article, I assume you have a basic familiarity with TypeScript, the three.js library and the set up of three.js programs.
Gradient Colored Line
Let’s start by creating a simple gradient colored three.js line. We will rely on WebGL to compute the color gradient of a line between its 2 end points (a.k.a. vertices).

To do this we use a BufferedGeometry into which we set the position and color of each end point. We do this by creating 2 BufferAttribute instances, one for the end point positions and one for the color of each end point. Next we create a simple LineBasicMaterial. We inform the material that the geometry will provide the color details for the end points. We complete the process by a new Line instance which is added to the scene for rendering.
// create geometry const geometry = new BufferGeometry(); // create line end points and add to geometry const vertices = new Float32Array([ 0, 0, 0, 50, 0, 0 ]); geometry.addAttribute('position', new BufferAttribute(vertices,3)); // create colors of each end point (vertex) and add to geometry const colors = new Float32Array([ 1.0, 0.0, 0.0, // red (normalized) 0.0, 1.0, 0.0 // green (normalized) ]); geometry.addAttribute('color', new BufferAttribute(colors,3)); // create material const material = new LineBasicMaterial({ vertexColors: VertexColors, // inform material that geometry // will provide color info linewidth: 4 // lineWidth not universally supported // works with safari }); // create line and position it const line = new Line(geometry, material); line.computeLineDistances(); // add line to scene so it can be rendered scene.add(line);
Multisegment Gradient Colored Lines
Creating a multi-segment gradient colored line is similar to creating a single segment line with the addition of internal vertices and their corresponding colors. Using a multisegment line you can create some neat line shapes. One requirement to note is that the geometry must have color data for each vertex.

// create geometry const geometry = new BufferGeometry(); // create line end points and add to geometry const vertices = new Float32Array([ 0, 0, 0, // (0,0,0) 20, 10, 0, // (20,10,0) 40, -10, 0, // (40,-10,0) 60, 0, 0 // (60,0,0) ]); geometry.addAttribute('position', new BufferAttribute(vertices,3)); // create colors of each end point (vertex) and add to geometry const colors = new Float32Array([ 1.0, 0.0, 0.0, // red (normalized) 1.0, 1.0, 0.0, // yellow (normalized) 0.0, 1.0, 1.0, // purple (normalized) 0.0, 0.0, 1.0, // blue (normalized) ]); geometry.addAttribute('color', new BufferAttribute(colors,3)); // create material const material = new LineBasicMaterial({ vertexColors: VertexColors, // inform material that geometry // will provide color info linewidth: 4 // lineWidth not universally supported // works with safari }); // create line const line = new Line(geometry, material); line.computeLineDistances(); // add line to scene so it can be rendered scene.add(line);
Gradient Colored CircleLine
Drawing a gradient colored circle is a little more involved as three.js does not provide a wireframe circle. The approach I’ve used is to compute a series of evenly spaced vertices that lie on the circle perimeter and fit a line through each of them to form a CircleLine. Gradient coloring of a CircleLine involves shading 1/2 of the CircleLine with interpolated colors between the startColor and endColor. Then mirror each color value onto the other 1/2 of the CircleLine.

const radius = 20; const segmentCount = 60 const segmentPoints = new Array<Vector3>(); // compute vertices on the perimeter of the circle const SEG_RADS = 2 * Math.PI / segmentCount; for (let i = 0; i < segmentCount; i++) { const x = radius * Math.cos(i * SEG_RADS); const y = radius * Math.sin(i * SEG_RADS); segmentPoints.push(new Vector3(x, y, 0)); } // create a smooth closed spline const stemCurve = new CatmullRomCurve3(segmentPoints, true); const splinePoints = stemCurve.getPoints(360); // create geometry using 360 points on the circle const geometry = new BufferGeometry().setFromPoints(splinePoints); // Need to create 1 gradient color for each vertex. // Color gradient for circle starts and ends with the startColor. // Compute gradient colors from startColor to endColor on 180 degress // using linear interpolation. // Then mirror colors on [0-180] degrees to [360-180] degrees const startColor = new Color('red'); const endColor = new Color('green'); const vertCnt = geometry.getAttribute('position').count; let hemisphereSegmentCount = Math.ceil(vertCnt / 2.0); const lerpInc = 1.0 / hemisphereSegmentCount; const colors = new Float32Array(vertCnt * 3); for (let i = 0; i <= hemisphereSegmentCount; i++) { const lerpColor = new Color(startColor); lerpColor.lerpHSL(endColor, i * lerpInc); colors[i * 3] = lerpColor.r; colors[i * 3 + 1] = lerpColor.g; colors[i * 3 + 2] = lerpColor.b; } // gradient color computed for top half of circle. // Now mirror top half colors onto bottom half of circle for (let i = 1, j = (vertCnt - 1) * 3; i < j; i += 3, j -= 3) { colors[j] = colors[i]; colors[j+1] = colors[i+1]; colors[j+2] = colors[i+2] } geometry.addAttribute('color', new BufferAttribute(colors, 3)); // create material const material = new LineBasicMaterial({ vertexColors: VertexColors, linewidth: 2 }); // create line const circle = new Line(geometry, material); circle.computeLineDistances(); scene.add(circle);
In my Groot Clock project I have factored the code above into a utility class to simplify its use. You can find all of the code for the project here.
In Part-3, I’ll discuss animating the drawing of lines in three.js. In the meantime please consider following me on twitter. All comments, suggestions and retweets will be greatly appreciated.