object looks translucent even though opacity is 1 in three js

Why is my three.js cube looking translucent when I change the metalness, roughness, and color even though opacity stays at 1.

The translucence only appears with a rectangle light, not a point light.

Watch the demo video and you’ll see the light appear on the outside as if the box was translucent.

<!DOCTYPE html>
<html>
<head>
    <title>Simple Rectangle Light</title>
    <style>
        body { margin: 0; }
        canvas { display: block; }
        #controls {
            position: fixed;
            top: 10px;
            left: 10px;
            background: rgba(0,0,0,0.7);
            padding: 10px;
            border-radius: 5px;
            color: white;
            font-family: Arial;
        }
    </style>
</head>
<body>
    <div id="controls">
        <label><input type="checkbox" id="rectLight" checked> Rectangle Light</label><br>
        <label><input type="checkbox" id="pointLight" checked> Point Light</label><br>
        <div>
            Rectangle Light Position:<br>
            X: <input type="range" id="rectX" min="-1" max="1" step="0.1" value="0"><br>
            Y: <input type="range" id="rectY" min="-1" max="0" step="0.1" value="-0.9"><br>
            Z: <input type="range" id="rectZ" min="-1" max="1" step="0.1" value="0"><br>
            Rectangle Light Rotation:<br>
            X: <input type="range" id="rectRotX" min="0" max="6.28" step="0.1" value="1.57"><br>
            Y: <input type="range" id="rectRotY" min="0" max="6.28" step="0.1" value="0"><br>
            Z: <input type="range" id="rectRotZ" min="0" max="6.28" step="0.1" value="0"><br>
            Rectangle Light Size:<br>
            Width: <input type="range" id="rectWidth" min="0.1" max="1.6" step="0.1" value="1.0"><br>
            Height: <input type="range" id="rectHeight" min="0.1" max="1.6" step="0.1" value="1.0">
        </div>
        <div>
            Material Properties:<br>
            Color: <input type="range" id="matColor" min="0" max="255" step="1" value="51"><br>
            Roughness: <input type="range" id="matRough" min="0" max="1" step="0.1" value="0.9"><br>
            Metalness: <input type="range" id="matMetal" min="0" max="1" step="0.1" value="0.0">
        </div>
    </div>
    <script type="importmap">
        {
            "imports": {
                "three": "https://unpkg.com/[email protected]/build/three.module.js",
                "three/examples/jsm/controls/OrbitControls.js": "https://unpkg.com/[email protected]/examples/jsm/controls/OrbitControls.js",
                "three/examples/jsm/lights/RectAreaLightUniformsLib.js": "https://unpkg.com/[email protected]/examples/jsm/lights/RectAreaLightUniformsLib.js",
                "three/examples/jsm/helpers/RectAreaLightHelper.js": "https://unpkg.com/[email protected]/examples/jsm/helpers/RectAreaLightHelper.js"
            }
        }
    </script>
    <script type="module">
        import * as THREE from 'three';
        import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
        import { RectAreaLightUniformsLib } from 'three/examples/jsm/lights/RectAreaLightUniformsLib.js';
        import { RectAreaLightHelper } from 'three/examples/jsm/helpers/RectAreaLightHelper.js';

        // Scene setup
        const scene = new THREE.Scene();
        scene.background = new THREE.Color(0x000000);

        // Camera setup
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.z = 5;

        // Renderer setup
        const renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // Initialize rect area light uniforms
        RectAreaLightUniformsLib.init();

        // Controls
        const controls = new OrbitControls(camera, renderer.domElement);

        // Comment out texture loading
        // const textureLoader = new THREE.TextureLoader();
        // const skinTexture = textureLoader.load('skin-image.png');

        // Add material definition before box creation
        const material = new THREE.MeshStandardMaterial({ 
            color: 0x333333,
            roughness: 0.9,
            metalness: 0.0,
            opacity: 1.0,
            transparent: false
        });

        // Create box
        const outerSize = 2;
        const thickness = 0.2;  // Back to original thickness
        const box = new THREE.Group();

        // Bottom
        const bottom = new THREE.Mesh(
            new THREE.BoxGeometry(outerSize, thickness, outerSize),
            material
        );
        bottom.position.y = -outerSize/2; 
        box.add(bottom);

        // Front wall
        const front = new THREE.Mesh(
            new THREE.BoxGeometry(outerSize, outerSize, thickness),
            material
        );
        front.position.z = outerSize/2 - thickness/2;
        front.position.y = -thickness/2;
        box.add(front);

        // Back wall
        const back = new THREE.Mesh(
            new THREE.BoxGeometry(outerSize, outerSize, thickness),
            material
        );
        back.position.z = -outerSize/2 + thickness/2;
        back.position.y = -thickness/2;
        box.add(back);

        // Left wall
        const left = new THREE.Mesh(
            new THREE.BoxGeometry(thickness, outerSize, outerSize),
            material
        );
        left.position.x = -outerSize/2 + thickness/2;
        left.position.y = -thickness/2;
        box.add(left);

        // Right wall
        const right = new THREE.Mesh(
            new THREE.BoxGeometry(thickness, outerSize, outerSize),
            material
        );
        right.position.x = outerSize/2 - thickness/2;
        right.position.y = -thickness/2;
        box.add(right);

        scene.add(box);

        // Rectangle light at bottom
        const width = 1.0;   // Smaller than inner width (was 1.8)
        const height = 1.0;  // Make it square like the box (was 1.8)
        const intensity = 10;
        const rectLight = new THREE.RectAreaLight(0xff0000, intensity, width, height);
        rectLight.position.set(0, -0.9 + 0.01, 0); // Keep it just above bottom
        rectLight.rotation.x = Math.PI / 2; // Face up
        scene.add(rectLight);

        // Add visible helper for the light
        const helper = new RectAreaLightHelper(rectLight);
        rectLight.add(helper);

        // Move ambient light before PMREM setup
        const ambientLight = new THREE.AmbientLight(0xffffff, 2.0);
        scene.add(ambientLight);

        // Add point light inside box
        const pointLight = new THREE.PointLight(0xff0000, 10.0);
        pointLight.position.set(0, -0.5, 0);
        pointLight.distance = 3;  // How far the light reaches
        pointLight.decay = 1;     // How quickly it fades with distance
        scene.add(pointLight);

        // Make the visualization sphere bigger
        const sphereGeometry = new THREE.SphereGeometry(0.2, 16, 16);  // Increase from 0.05 to 0.2
        const sphereMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
        const lightSphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
        lightSphere.position.copy(pointLight.position);
        scene.add(lightSphere);

        // Animation
        function animate() {
            requestAnimationFrame(animate);
            controls.update();
            renderer.render(scene, camera);
        }
        animate();

        // Handle window resize
        window.addEventListener('resize', () => {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        });

        document.getElementById('rectLight').addEventListener('change', (e) => {
            rectLight.visible = e.target.checked;
            helper.visible = e.target.checked;
        });

        document.getElementById('pointLight').addEventListener('change', (e) => {
            pointLight.visible = e.target.checked;
            lightSphere.visible = e.target.checked;
        });

        document.getElementById('rectX').addEventListener('input', (e) => {
            rectLight.position.x = parseFloat(e.target.value);
            updateURL();
        });

        document.getElementById('rectY').addEventListener('input', (e) => {
            rectLight.position.y = parseFloat(e.target.value) + 0.01; // Keep slight offset from bottom
        });

        document.getElementById('rectZ').addEventListener('input', (e) => {
            rectLight.position.z = parseFloat(e.target.value);
        });

        document.getElementById('rectRotX').addEventListener('input', (e) => {
            rectLight.rotation.x = parseFloat(e.target.value);
        });

        document.getElementById('rectRotY').addEventListener('input', (e) => {
            rectLight.rotation.y = parseFloat(e.target.value);
        });

        document.getElementById('rectRotZ').addEventListener('input', (e) => {
            rectLight.rotation.z = parseFloat(e.target.value);
        });

        document.getElementById('rectWidth').addEventListener('input', (e) => {
            rectLight.width = parseFloat(e.target.value);
            helper.update(); // Update the helper to show new size
        });

        document.getElementById('rectHeight').addEventListener('input', (e) => {
            rectLight.height = parseFloat(e.target.value);
            helper.update(); // Update the helper to show new size
        });

        document.getElementById('matColor').addEventListener('input', (e) => {
            const value = parseInt(e.target.value);
            material.color.setRGB(value/255, value/255, value/255);
            updateURL();
        });

        document.getElementById('matRough').addEventListener('input', (e) => {
            material.roughness = parseFloat(e.target.value);
        });

        document.getElementById('matMetal').addEventListener('input', (e) => {
            material.metalness = parseFloat(e.target.value);
        });

        // Add function to update URL with current settings
        function updateURL() {
            const params = new URLSearchParams();
            
            // Material settings
            params.set('color', document.getElementById('matColor').value);
            params.set('rough', document.getElementById('matRough').value);
            params.set('metal', document.getElementById('matMetal').value);
            
            // Light visibility
            params.set('rectVisible', document.getElementById('rectLight').checked);
            params.set('pointVisible', document.getElementById('pointLight').checked);
            
            // Rectangle light settings
            params.set('rectX', document.getElementById('rectX').value);
            params.set('rectY', document.getElementById('rectY').value);
            params.set('rectZ', document.getElementById('rectZ').value);
            params.set('rectRotX', document.getElementById('rectRotX').value);
            params.set('rectRotY', document.getElementById('rectRotY').value);
            params.set('rectRotZ', document.getElementById('rectRotZ').value);
            params.set('rectWidth', document.getElementById('rectWidth').value);
            params.set('rectHeight', document.getElementById('rectHeight').value);
            
            window.history.replaceState({}, '', `${window.location.pathname}?${params.toString()}`);
        }

        // Also add function to load settings from URL on page load
        function loadFromURL() {
            const params = new URLSearchParams(window.location.search);
            
            // Helper function to load a parameter
            function loadParam(id, prop, obj, converter = parseFloat) {
                if(params.has(id)) {
                    const value = converter(params.get(id));
                    document.getElementById(id).value = value;
                    if(obj && prop) obj[prop] = value;
                }
            }
            
            // Load all parameters
            loadParam('color', null, null, value => {
                const v = parseInt(value);
                material.color.setRGB(v/255, v/255, v/255);
                return v;
            });
            loadParam('rough', 'roughness', material);
            loadParam('metal', 'metalness', material);
            loadParam('rectX', 'x', rectLight.position);
            // ... etc for all parameters
        }

        // Call on page load
        loadFromURL();
    </script>
</body>
</html>