What Are Environment Maps?
An environment map is an image that wraps around your entire scene, representing the surrounding world. It serves two purposes: it can act as a background (what the viewer sees behind your objects) and as a light source (what your materials reflect and what illuminates their surfaces). Environment maps are the fastest way to achieve realistic reflections and lighting without placing dozens of individual lights.
In the real world, objects are lit by everything around them: the sky, the ground, nearby walls, distant buildings. Environment maps capture this omnidirectional lighting in a single image. When applied to a scene, they provide the kind of nuanced, natural illumination that point lights and directional lights alone can't match.
Types of Environment Maps
Three.js supports two primary formats for environment maps:
- Cube maps — Six square images arranged as the faces of a cube (positive X, negative X, positive Y, negative Y, positive Z, negative Z). This is the classic format, but managing six separate files is cumbersome.
- Equirectangular maps — A single panoramic image that wraps around a sphere, like a Mercator projection of the sky. This is the modern standard. Most HDR environment maps you'll find online use this format.
The equirectangular format has largely won out. It's a single file, easier to work with, and Three.js handles the conversion to a cube map internally via the PMREMGenerator. For the rest of this article, we'll focus on equirectangular HDR maps.
Loading an HDR Environment Map
HDR (High Dynamic Range) images store brightness values beyond the 0–255 range of standard images. This extra range is critical for realistic lighting because it preserves the intensity differences between, say, the sun and the sky around it. Three.js uses the RGBELoader to load .hdr files.
import * as THREE from 'three';
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.0;
const pmremGenerator = new THREE.PMREMGenerator(renderer);
pmremGenerator.compileEquirectangularShader();
new RGBELoader().load('/path/to/environment.hdr', (texture) => {
const envMap = pmremGenerator.fromEquirectangular(texture).texture;
// Use as scene background
scene.background = envMap;
// Use as lighting source for all materials
scene.environment = envMap;
// Clean up
texture.dispose();
pmremGenerator.dispose();
});The PMREMGenerator (Prefiltered Mipmapped Radiance Environment Map Generator) processes the raw HDR texture into a format optimized for physically based materials. It pre-computes multiple blur levels so that rough surfaces reflect a blurry version of the environment while smooth surfaces reflect it sharply. Without this step, all reflections would be mirror-sharp regardless of material roughness.
Environment Map as Background
Setting scene.background to an environment map texture places the panoramic image behind everything in your scene. The viewer sees the environment surrounding the objects, giving the impression that the scene exists in a real location.
The demo above shows a metallic sphere placed inside an HDR environment. The environment map serves as both the visible background and the source of reflections on the sphere's surface. Notice how the sphere picks up colors and light from the surrounding environment, not from any placed lights.
// Setting the environment map as background
scene.background = envMap;
// You can also control the background blur
scene.backgroundBlurriness = 0.0; // 0 = sharp, 1 = fully blurred
scene.backgroundIntensity = 1.0; // brightness of the backgroundEnvironment Map as Lighting
Setting scene.environment tells Three.js to use the environment map as the lighting source for all MeshStandardMaterial and MeshPhysicalMaterial objects in the scene. This is called image-based lighting (IBL). Every surface samples the environment map to determine what light is hitting it and what it should reflect.
This demo shows three spheres with different roughness values, all lit exclusively by the environment map (no directional or point lights). The leftmost sphere is mirror-smooth and shows crisp reflections. The middle sphere is moderately rough, showing blurred reflections. The rightmost sphere is very rough, reflecting only broad color tones from the environment. This is the PMREMGenerator at work: each roughness level samples a different pre-filtered mip level of the environment map.
// Use environment map for lighting all PBR materials
scene.environment = envMap;
// You can also set envMap per-material for more control:
const material = new THREE.MeshStandardMaterial({
color: 0xffffff,
metalness: 1.0,
roughness: 0.0,
envMap: envMap, // override scene.environment
envMapIntensity: 1.5 // boost reflections on this material
});Separating Background from Lighting
You don't have to use the same map for both background and lighting. A common technique is to use an environment map for lighting but set the background to a solid color or a blurred version of the map. This keeps the lighting realistic while giving you creative control over the backdrop.
Here the objects are lit by an HDR environment map, but the background is a plain dark color. The reflections on the metallic surfaces still show the environment, yet the background is clean and uncluttered. This is a common setup for product visualization and portfolio pieces.
// Lighting from environment map, plain background
scene.environment = envMap;
scene.background = new THREE.Color(0x111111);
// Or use a blurred version of the same map
scene.background = envMap;
scene.backgroundBlurriness = 0.8; // Soft, out-of-focus backgroundMaterial Properties and Environment Maps
How strongly an environment map affects a material depends on three properties: metalness, roughness, and envMapIntensity. Understanding these interactions is essential for getting the look you want.
The grid above shows spheres across a range of metalness (left to right) and roughness (top to bottom). Metallic surfaces reflect the environment directly as their color. Non-metallic (dielectric) surfaces show the environment only in their specular highlights. Higher roughness blurs the reflection regardless of metalness. The envMapIntensity property scales how bright the environment reflection appears.
// Metallic, mirror-smooth: strong, crisp reflections
const chrome = new THREE.MeshStandardMaterial({
color: 0xffffff,
metalness: 1.0,
roughness: 0.0,
envMapIntensity: 1.0
});
// Non-metallic, smooth: subtle reflections in highlights
const plastic = new THREE.MeshStandardMaterial({
color: 0xff4444,
metalness: 0.0,
roughness: 0.2,
envMapIntensity: 1.0
});
// Rough metal: broad, blurred color from environment
const brushedMetal = new THREE.MeshStandardMaterial({
color: 0xcccccc,
metalness: 1.0,
roughness: 0.6,
envMapIntensity: 1.0
});Loading Cube Map Environments
While equirectangular HDR maps are the modern standard, you may encounter cube maps, especially in older tutorials or game assets. A cube map consists of six images representing the faces of a cube: px, nx, py, ny, pz, nz (positive/negative X, Y, Z).
import { CubeTextureLoader } from 'three';
const cubeLoader = new CubeTextureLoader();
cubeLoader.setPath('/textures/cubemap/');
const cubeMap = cubeLoader.load([
'px.png', 'nx.png', // right, left
'py.png', 'ny.png', // top, bottom
'pz.png', 'nz.png' // front, back
]);
scene.background = cubeMap;
scene.environment = cubeMap;
// Cube maps work directly without PMREMGenerator,
// but for proper roughness-based reflections you should
// still process them:
const envMap = pmremGenerator.fromCubemap(cubeMap).texture;
scene.environment = envMap;Performance Considerations
Environment maps are one of the best performance bargains in 3D rendering. A single HDR map replaces potentially dozens of light sources, and the PMREMGenerator does its heavy work once at load time rather than every frame. That said, there are a few things to watch:
- Texture resolution: HDR environment maps are large. A 4K equirectangular map is common and works well for most cases. Going to 8K or higher increases load time and GPU memory without much visible benefit unless you're displaying the background at full screen with no blur.
- PMREMGenerator cost: The generation step happens once, but it's not free. On low-end GPUs it can cause a brief stutter. Call
pmremGenerator.compileEquirectangularShader()early to spread the cost. - Tone mapping: HDR environments require tone mapping to display correctly on standard screens.
THREE.ACESFilmicToneMappingis the most common choice. Without it, bright areas will clip to white and you'll lose the dynamic range that makes HDR valuable. - Dispose when done: Always dispose of the source texture and the PMREMGenerator after creating the environment map. The processed texture lives on the GPU; the source data is no longer needed.
// Proper cleanup pattern
new RGBELoader().load('/environment.hdr', (texture) => {
const envMap = pmremGenerator.fromEquirectangular(texture).texture;
scene.background = envMap;
scene.environment = envMap;
// Free the source texture and generator
texture.dispose();
pmremGenerator.dispose();
});
// Tone mapping is essential for HDR
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.0; // adjust to taste
// Other tone mapping options:
// THREE.LinearToneMapping — no adjustment (clips HDR)
// THREE.ReinhardToneMapping — soft rolloff
// THREE.CineonToneMapping — film-like response
// THREE.ACESFilmicToneMapping — industry standard, best defaultWhere to Find Environment Maps
You don't need to create your own HDR panoramas. Several sites offer free, high-quality environment maps licensed for commercial use:
- Poly Haven (polyhaven.com/hdris) — Hundreds of free CC0-licensed HDR environments. This is the go-to source for most Three.js developers. Maps are available in multiple resolutions.
- ambientCG (ambientcg.com) — Another large collection of CC0 HDR environments alongside PBR materials and textures.
- iHDRI (ihdri.com) — Curated HDR panoramas, free for personal and commercial use.
Download in .hdr format for use with RGBELoader. A 2K resolution (2048 x 1024) is sufficient for lighting-only use. Use 4K if the map will also serve as a visible background.