Three.js InstancedMesh Custom Color Attribute

This article explains how to structure and apply a custom attribute array to assign individual colors to instances within a Three.js InstancedMesh. You will learn how to define a flat Float32Array containing RGB values, wrap it in an InstancedBufferAttribute, attach it to your geometry, and configure your material to display these custom colors efficiently.

The Structure of the Color Array

To color individual instances, you must create a flat array of floating-point numbers. Since colors are typically represented by Red, Green, and Blue (RGB) channels, your array must have a size equal to the number of instances multiplied by 3.

const count = 1000; // Number of instances
const colorArray = new Float32Array(count * 3);

For each instance i (from 0 to count - 1), you fill the array with normalized RGB values (ranging from 0.0 to 1.0):

Implementation Step-by-Step

1. Define the Geometry and Material

Create your base geometry and a material. To tell the material to use the colors defined in your attribute array, you must set the vertexColors property to true.

import * as THREE from 'three';

const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({
    vertexColors: true
});

2. Populate the Color Array

Loop through your total instance count to assign a color to each instance. In this example, we generate random colors:

const count = 500;
const colorArray = new Float32Array(count * 3);

for (let i = 0; i < count; i++) {
    const randomColor = new THREE.Color(Math.random(), Math.random(), Math.random());
    
    colorArray[i * 3] = randomColor.r;     // R
    colorArray[i * 3 + 1] = randomColor.g; // G
    colorArray[i * 3 + 2] = randomColor.b; // B
}

3. Create the InstancedBufferAttribute

Wrap your raw Float32Array in a THREE.InstancedBufferAttribute. The second argument is 3, which tells Three.js that each instance requires three values (R, G, and B) from the array.

const colorAttribute = new THREE.InstancedBufferAttribute(colorArray, 3);

4. Attach the Attribute to Geometry

Attach your color attribute to your geometry using the key name 'color'. This name is recognized by Three.js built-in materials.

geometry.setAttribute('color', colorAttribute);

5. Create the InstancedMesh

Finally, instantiate your InstancedMesh using the modified geometry and material.

const instancedMesh = new THREE.InstancedMesh(geometry, material, count);
scene.add(instancedMesh);

Updating Colors Dynamically

If you need to update the colors after rendering has started, modify the values in the original Float32Array or access the attribute’s array directly, and set the attribute’s needsUpdate flag to true:

// Modify the color of the first instance to pure red
colorAttribute.setXYZ(0, 1.0, 0.0, 0.0);

// Notify Three.js to upload the new data to the GPU
colorAttribute.needsUpdate = true;