In the BIM clash viewer, clicking a clash isolates the offending pair and ghosts the rest of the model to glass — the way a coordination review reads a conflict through the surrounding structure. It worked for some elements and silently did nothing for others.
The symptom
The ghosting code was trivial: for every element that isn't in the clash, set material.opacity = 0.07. Elements that were already translucent (the architecture layer) faded correctly. Elements that started fully opaque (columns, beams) stayed solid — the same line, ignored.
The cause
three.js compiles a material into a GPU shader program once, and whether the material is transparent is baked into that program at compile time — it changes the render pass, blending and depth-write behaviour. Flipping material.transparent = true at runtime doesn't recompile the program, so a material that was born opaque keeps rendering opaque no matter what you do to opacity.
The fix is one line — force the recompile:
material.transparent = truematerial.opacity = 0.07material.needsUpdate = true← the line that was missing
The nuance worth keeping
Not every property behaves this way. color and opacity are shader uniforms — live, cheap, per-frame. transparent, clipping planes, and light counts are compile-time — changing them costs a program rebuild. So needsUpdate is a sledgehammer: right once on a state change, wrong every frame in the render loop.
In retained-mode 3D, "set a property" isn't always "and it takes effect." Know which properties are uniforms and which recompile the shader.