Three.js Color Space Conversion Guide

Managing color spaces correctly is crucial for achieving realistic rendering in 3D web applications. This article provides a straightforward guide on how to perform color space conversions using the built-in Color class in Three.js, ensuring your colors look consistent across different devices and render pipelines.

Modern Three.js Color Management

In modern Three.js (version r152 and later), color management is enabled by default via THREE.ColorManagement.enabled = true. This setting ensures that Three.js automatically converts color inputs—such as CSS strings, hex values, and RGB components—from the sRGB color space into the Linear-sRGB working color space used for lighting calculations.

Manual Conversion with the Color Class

If you need to perform manual color space conversions, the THREE.Color class provides built-in utility methods. These are essential when working with raw shaders or legacy codebases where automatic color management is disabled.

1. Converting sRGB to Linear Space

To convert a color from the standard sRGB space (used by most digital displays and image formats) to linear space for rendering calculations:

import * as THREE from 'three';

// Create a color using sRGB values (e.g., bright red)
const color = new THREE.Color('#ff0000');

// Convert the color from sRGB to Linear-sRGB
color.convertSRGBToLinear();

2. Converting Linear to sRGB Space

To convert a color from the linear working space back to sRGB (useful for UI overlays, exporting data, or console logging):

// Convert the color from Linear-sRGB back to sRGB
color.convertLinearToSRGB();

Best Practices for Textures and Renderer

For correct color space rendering, your manual conversions must align with your renderer and texture settings.

Renderer Configuration

Ensure your WebGLRenderer is configured to output the sRGB color space:

const renderer = new THREE.WebGLRenderer();
renderer.outputColorSpace = THREE.SRGBColorSpace;

Texture Color Space

Always specify the correct color space for your textures. Color maps (such as diffuse or albedo textures) should use SRGBColorSpace, while data-centric maps (such as normal, roughness, or metalness maps) must remain in NoColorSpace (linear):

const textureLoader = new THREE.TextureLoader();

// Color texture (requires sRGB conversion)
const colorTexture = textureLoader.load('color.jpg');
colorTexture.colorSpace = THREE.SRGBColorSpace;

// Normal map (remains linear)
const normalTexture = textureLoader.load('normal.jpg');
normalTexture.colorSpace = THREE.NoColorSpace;