DEV Community

Cover image for The color-change memory leak in Three.js configurators
Dominik
Dominik

Posted on

The color-change memory leak in Three.js configurators

Color pickers are the most-used control in a product configurator. They are also where I shipped my first real GPU memory leak, because the fix for "change the color" looks harmless and is not.

The leak

The intuitive version builds a fresh material every time the user picks a color:

colorPicker.addEventListener('input', (e) => {
  mesh.material = new THREE.MeshStandardMaterial({ color: e.target.value })
})
Enter fullscreen mode Exit fullscreen mode

It works on screen. But the old material is still on the GPU. Three.js does not garbage-collect GPU resources for you. A customer who tries twenty colors has left nineteen materials, and their textures and shader programs, allocated on the graphics card. On a configurator people play with for a few minutes, memory climbs until the tab gets slow or the context is lost.

The fix: reuse the material, set the color

If only the color changes, never build a new material:

colorPicker.addEventListener('input', (e) => {
  mesh.material.color.set(e.target.value)
})
Enter fullscreen mode Exit fullscreen mode

One material for the lifetime of the mesh. The color is a cheap uniform update, not a new GPU allocation. This is also why per-instance color on an InstancedMesh uses setColorAt, not a new material per instance.

When you really do need a new material

Sometimes the change is not just color: a different map, a switch from matte to glossy, transparent to solid. Then you do need a different material, and you must dispose of the old one yourself:

const old = mesh.material
mesh.material = buildMaterial(newOptions)
old.dispose()                 // frees the program and uniforms
old.map?.dispose()            // textures are separate, dispose them too
Enter fullscreen mode Exit fullscreen mode

The rule: .dispose() anything you replace, and do not replace what you can mutate. Color, roughness, metalness, opacity are all live properties. Reach for a new material only when the shader itself has to change.

The decorative lamp configurator at lampy3d.grodev.pl swaps finishes constantly and holds flat memory because of exactly this.

I am Dominik Groński / GroDev, a studio in Poznan, Poland building custom 3D configurators and Laravel back-ends for manufacturers.

Top comments (0)