How to Use Three.js DataTexture for Procedural Textures
This article explains how to generate procedural textures dynamically
in JavaScript using the DataTexture class in Three.js. You
will learn how to initialize raw pixel buffers, populate them with
mathematical patterns, and apply the resulting procedural texture to a
3D mesh material without needing to load external image files.
Understanding Three.js DataTexture
A DataTexture in Three.js allows you to create a texture
directly from a raw array of pixel values. This is ideal for generating
procedural patterns like noise, gradients, grids, or cellular automata
at runtime.
Unlike a standard Texture that loads an image file
(PNG/JPG), DataTexture consumes a typed array, usually a
Uint8Array (for 8-bit color channels) or a
Float32Array (for high-dynamic-range data).
Step-by-Step Implementation
Step 1: Define the Texture Dimensions
First, specify the width and height of your texture. For optimal compatibility and performance with mipmapping, use powers of two (e.g., 128, 256, 512).
const width = 256;
const height = 256;
const size = width * height;Step 2: Create and Populate the Typed Array
Create a Uint8Array to hold the RGBA color data. Because
each pixel has four channels (Red, Green, Blue, Alpha), the size of the
array must be width * height * 4.
Next, run a loop to calculate the color of each pixel procedurally. In this example, we generate a smooth color gradient:
const data = new Uint8Array(4 * size);
for (let i = 0; i < size; i++) {
const x = i % width;
const y = Math.floor(i / width);
// Calculate normalized coordinates (0 to 1)
const u = x / width;
const v = y / height;
const stride = i * 4;
// Procedural color formulas
data[stride] = Math.floor(u * 255); // Red
data[stride + 1] = Math.floor(v * 255); // Green
data[stride + 2] = 128; // Blue (constant)
data[stride + 3] = 255; // Alpha (fully opaque)
}Step 3: Instantiate the DataTexture
Pass the data buffer, width, height, and color format to the
THREE.DataTexture constructor.
import * as THREE from 'three';
const texture = new THREE.DataTexture(data, width, height, THREE.RGBAFormat);Step 4: Flag the Texture for Update
When you modify or create raw data for a texture, you must notify Three.js that the texture data is ready to be uploaded to the GPU.
texture.needsUpdate = true;Step 5: Apply the Texture to a Material
Now, you can use the procedural texture just like any standard
texture by assigning it to a material’s map property.
const material = new THREE.MeshBasicMaterial({ map: texture });
const geometry = new THREE.BoxGeometry(1, 1, 1);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);Updating Procedural Textures Dynamically
If you need to animate or change the procedural texture over time,
you can modify the existing data array in your animation
loop and set texture.needsUpdate = true on each frame.
function animate() {
requestAnimationFrame(animate);
// Example: shift colors over time
const time = performance.now() * 0.001;
for (let i = 0; i < size; i++) {
const stride = i * 4;
data[stride] = Math.floor((Math.sin(time + i * 0.01) + 1) * 127);
}
texture.needsUpdate = true;
renderer.render(scene, camera);
}