Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Three.js lighting - light types, shadows, environment lighting. Use when adding lights, configuring shadows, setting up IBL, or optimizing lighting performance.
.claude/skills/bilal140202-threejs-lighting/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-19 | ✗→✓ | ▲ Improved | 220% | 0% |
| case-01 | ✓→✓ | = Same ✓ | 154% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 338% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 231% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 372% | 0% |
javascriptimport * as THREE from "three"; // Basic lighting setup const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); scene.add(ambientLight); const directionalLight = new THREE.DirectionalLight(0xffffff, 1); directionalLight.position.set(5, 5, 5); scene.add(directionalLight);
| Light | Description | Shadow Support | Cost | | ---------------- | ---------------------- | -------------- | -------- | | AmbientLight | Uniform everywhere | No | Very Low | | HemisphereLight | Sky/ground gradient | No | Very Low | | DirectionalLight | Parallel rays (sun) | Yes | Low | | PointLight | Omnidirectional (bulb) | Yes | Medium | | SpotLight | Cone-shaped | Yes | Medium | | RectAreaLight | Area light (window) | No\ | High |
\RectAreaLight shadows require custom solutions
Illuminates all objects equally. No direction, no shadows.
javascript// AmbientLight(color, intensity) const ambient = new THREE.AmbientLight(0xffffff, 0.5); scene.add(ambient); // Modify at runtime ambient.color.set(0xffffcc); ambient.intensity = 0.3;
Gradient from sky to ground color. Good for outdoor scenes.
javascript// HemisphereLight(skyColor, groundColor, intensity) const hemi = new THREE.HemisphereLight(0x87ceeb, 0x8b4513, 0.6); hemi.position.set(0, 50, 0); scene.add(hemi); // Properties hemi.color; // Sky color hemi.groundColor; // Ground color hemi.intensity;
Parallel light rays. Simulates distant light source (sun).
javascript// DirectionalLight(color, intensity) const dirLight = new THREE.DirectionalLight(0xffffff, 1); dirLight.position.set(5, 10, 5); // Light points at target (default: 0, 0, 0) dirLight.target.position.set(0, 0, 0); scene.add(dirLight.target); scene.add(dirLight);
javascriptdirLight.castShadow = true; // Shadow map size (higher = sharper, more expensive) dirLight.shadow.mapSize.width = 2048; dirLight.shadow.mapSize.height = 2048; // Shadow camera (orthographic) dirLight.shadow.camera.near = 0.5; dirLight.shadow.camera.far = 50; dirLight.shadow.camera.left = -10; dirLight.shadow.camera.right = 10; dirLight.shadow.camera.top = 10; dirLight.shadow.camera.bottom = -10; // Shadow softness dirLight.shadow.radius = 4; // Blur radius (PCFSoftShadowMap only) // Shadow bias (fixes shadow acne) dirLight.shadow.bias = -0.0001; dirLight.shadow.normalBias = 0.02; // Helper to visualize shadow camera const helper = new THREE.CameraHelper(dirLight.shadow.camera); scene.add(helper);
Emits light in all directions from a point. Like a light bulb.
javascript// PointLight(color, intensity, distance, decay) const pointLight = new THREE.PointLight(0xffffff, 1, 100, 2); pointLight.position.set(0, 5, 0); scene.add(pointLight); // Properties pointLight.distance; // Maximum range (0 = infinite) pointLight.decay; // Light falloff (physically correct = 2)
javascriptpointLight.castShadow = true; pointLight.shadow.mapSize.width = 1024; pointLight.shadow.mapSize.height = 1024; // Shadow camera (perspective - 6 directions for cube map) pointLight.shadow.camera.near = 0.5; pointLight.shadow.camera.far = 50; pointLight.shadow.bias = -0.005;
Cone-shaped light. Like a flashlight or stage light.
javascript// SpotLight(color, intensity, distance, angle, penumbra, decay) const spotLight = new THREE.SpotLight(0xffffff, 1, 100, Math.PI / 6, 0.5, 2); spotLight.position.set(0, 10, 0); // Target (light points at this) spotLight.target.position.set(0, 0, 0); scene.add(spotLight.target); scene.add(spotLight); // Properties spotLight.angle; // Cone angle (radians, max Math.PI/2) spotLight.penumbra; // Soft edge (0-1) spotLight.distance; // Range spotLight.decay; // Falloff
javascriptspotLight.castShadow = true; spotLight.shadow.mapSize.width = 1024; spotLight.shadow.mapSize.height = 1024; // Shadow camera (perspective) spotLight.shadow.camera.near = 0.5; spotLight.shadow.camera.far = 50; spotLight.shadow.camera.fov = 30; spotLight.shadow.bias = -0.0001; // Focus (affects shadow projection) spotLight.shadow.focus = 1;
Rectangular area light. Great for soft, realistic lighting.
javascriptimport { RectAreaLightHelper } from "three/examples/jsm/helpers/RectAreaLightHelper.js"; import { RectAreaLightUniformsLib } from "three/examples/jsm/lights/RectAreaLightUniformsLib.js"; // Must initialize uniforms first RectAreaLightUniformsLib.init(); // RectAreaLight(color, intensity, width, height) const rectLight = new THREE.RectAreaLight(0xffffff, 5, 4, 2); rectLight.position.set(0, 5, 0); rectLight.lookAt(0, 0, 0); scene.add(rectLight); // Helper const helper = new RectAreaLightHelper(rectLight); rectLight.add(helper); // Note: Only works with MeshStandardMaterial and MeshPhysicalMaterial // Does not cast shadows natively
javascript// 1. Enable on renderer renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; // Shadow map types: // THREE.BasicShadowMap - fastest, low quality // THREE.PCFShadowMap - default, filtered // THREE.PCFSoftShadowMap - softer edges // THREE.VSMShadowMap - variance shadow map // 2. Enable on light light.castShadow = true; // 3. Enable on objects mesh.castShadow = true; mesh.receiveShadow = true; // Ground plane floor.receiveShadow = true; floor.castShadow = false; // Usually false for floors
javascript// Tight shadow camera frustum const d = 10; dirLight.shadow.camera.left = -d; dirLight.shadow.camera.right = d; dirLight.shadow.camera.top = d; dirLight.shadow.camera.bottom = -d; dirLight.shadow.camera.near = 0.5; dirLight.shadow.camera.far = 30; // Fix shadow acne dirLight.shadow.bias = -0.0001; // Depth bias dirLight.shadow.normalBias = 0.02; // Bias along normal // Shadow map size (balance quality vs performance) // 512 - low quality // 1024 - medium quality // 2048 - high quality // 4096 - very high quality (expensive)
javascriptimport { ContactShadows } from "three/examples/jsm/objects/ContactShadows.js"; const contactShadows = new ContactShadows({ resolution: 512, blur: 2, opacity: 0.5, scale: 10, position: [0, 0, 0], }); scene.add(contactShadows);
javascriptimport { RectAreaLightHelper } from "three/examples/jsm/helpers/RectAreaLightHelper.js"; // DirectionalLight helper const dirHelper = new THREE.DirectionalLightHelper(dirLight, 5); scene.add(dirHelper); // PointLight helper const pointHelper = new THREE.PointLightHelper(pointLight, 1); scene.add(pointHelper); // SpotLight helper const spotHelper = new THREE.SpotLightHelper(spotLight); scene.add(spotHelper); // Hemisphere helper const hemiHelper = new THREE.HemisphereLightHelper(hemiLight, 5); scene.add(hemiHelper); // RectAreaLight helper const rectHelper = new RectAreaLightHelper(rectLight); rectLight.add(rectHelper); // Update helpers when light changes dirHelper.update(); spotHelper.update();
Image-Based Lighting using HDR environment maps.
javascriptimport { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js"; const rgbeLoader = new RGBELoader(); rgbeLoader.load("environment.hdr", (texture) => { texture.mapping = THREE.EquirectangularReflectionMapping; // Set as scene environment (affects all PBR materials) scene.environment = texture; // Optional: also use as background scene.background = texture; scene.backgroundBlurriness = 0; // 0-1, blur the background scene.backgroundIntensity = 1; }); // PMREMGenerator for better reflections const pmremGenerator = new THREE.PMREMGenerator(renderer); pmremGenerator.compileEquirectangularShader(); rgbeLoader.load("environment.hdr", (texture) => { const envMap = pmremGenerator.fromEquirectangular(texture).texture; scene.environment = envMap; texture.dispose(); pmremGenerator.dispose(); });
javascriptconst cubeLoader = new THREE.CubeTextureLoader(); const envMap = cubeLoader.load([ "px.jpg", "nx.jpg", "py.jpg", "ny.jpg", "pz.jpg", "nz.jpg", ]); scene.environment = envMap; scene.background = envMap;
Capture lighting from a point in space for ambient lighting.
javascriptimport { LightProbeGenerator } from "three/examples/jsm/lights/LightProbeGenerator.js"; // Generate from cube texture const lightProbe = new THREE.LightProbe(); scene.add(lightProbe); lightProbe.copy(LightProbeGenerator.fromCubeTexture(cubeTexture)); // Or from render target const cubeCamera = new THREE.CubeCamera( 0.1, 100, new THREE.WebGLCubeRenderTarget(256), ); cubeCamera.update(renderer, scene); lightProbe.copy( LightProbeGenerator.fromCubeRenderTarget(renderer, cubeCamera.renderTarget), );
javascript// Key light (main light) const keyLight = new THREE.DirectionalLight(0xffffff, 1); keyLight.position.set(5, 5, 5); scene.add(keyLight); // Fill light (softer, opposite side) const fillLight = new THREE.DirectionalLight(0xffffff, 0.5); fillLight.position.set(-5, 3, 5); scene.add(fillLight); // Back light (rim lighting) const backLight = new THREE.DirectionalLight(0xffffff, 0.3); backLight.position.set(0, 5, -5); scene.add(backLight); // Ambient fill const ambient = new THREE.AmbientLight(0x404040, 0.3); scene.add(ambient);
javascript// Sun const sun = new THREE.DirectionalLight(0xffffcc, 1.5); sun.position.set(50, 100, 50); sun.castShadow = true; scene.add(sun); // Sky ambient const hemi = new THREE.HemisphereLight(0x87ceeb, 0x8b4513, 0.6); scene.add(hemi);
javascript// Multiple area lights RectAreaLightUniformsLib.init(); const light1 = new THREE.RectAreaLight(0xffffff, 5, 2, 2); light1.position.set(3, 3, 3); light1.lookAt(0, 0, 0); scene.add(light1); const light2 = new THREE.RectAreaLight(0xffffff, 3, 2, 2); light2.position.set(-3, 3, 3); light2.lookAt(0, 0, 0); scene.add(light2); // Ambient fill const ambient = new THREE.AmbientLight(0x404040, 0.2); scene.add(ambient);
javascriptconst clock = new THREE.Clock(); function animate() { const time = clock.getElapsedTime(); // Orbit light around scene light.position.x = Math.cos(time) * 5; light.position.z = Math.sin(time) * 5; // Pulsing intensity light.intensity = 1 + Math.sin(time * 2) * 0.5; // Color cycling light.color.setHSL((time * 0.1) % 1, 1, 0.5); // Update helpers if using lightHelper.update(); }
javascript// Light layers light.layers.set(1); // Light only affects layer 1 mesh.layers.enable(1); // Mesh is on layer 1 otherMesh.layers.disable(1); // Other mesh not affected // Selective shadows mesh.castShadow = true; mesh.receiveShadow = true; decorMesh.castShadow = false; // Small objects often don't need to cast
threejs-materials - Material light responsethreejs-textures - Lightmaps and environment mapsthreejs-postprocessing - Bloom and other light effects| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | pass→pass | 8,583 | 4,047 | -53% | 1 | 1 | 0% | 1,823 | 4,625 | +154% | 0 | 0 | — |
case-02 | pass→pass | 5,055 | 3,409 | -33% | 1 | 1 | 0% | 1,015 | 4,443 | +338% | 0 | 0 | — |
case-03 | pass→pass | 6,795 | 4,500 | -34% | 1 | 1 | 0% | 1,354 | 4,479 | +231% | 0 | 0 | — |
case-04 | pass→pass | 4,054 | 3,121 | -23% | 1 | 1 | 0% | 939 | 4,432 | +372% | 0 | 0 | — |
case-05 | pass→pass | 5,524 | 5,388 | -2% | 1 | 1 | 0% | 1,293 | 4,921 | +281% | 0 | 0 | — |
case-06 | pass→pass | 5,830 | 4,991 | -14% | 1 | 1 | 0% | 1,319 | 4,886 | +270% | 0 | 0 | — |
case-07 | fail→fail | 6,373 | 5,792 | -9% | 1 | 1 | 0% | 1,136 | 4,839 | +326% | 0 | 0 | — |
case-08 | pass→pass | 5,787 | 4,098 | -29% | 1 | 1 | 0% | 1,144 | 4,456 | +290% | 0 | 0 | — |
case-09 | pass→pass | 7,672 | 7,302 | -5% | 1 | 1 | 0% | 1,620 | 5,059 | +212% | 0 | 0 | — |
case-10 | pass→pass | 15,949 | 10,090 | -37% | 1 | 1 | 0% | 3,678 | 5,901 | +60% | 0 | 0 | — |
case-11 | fail→fail | 13,751 | 9,760 | -29% | 1 | 1 | 0% | 2,811 | 5,755 | +105% | 0 | 0 | — |
case-12 | pass→pass | 4,006 | 4,997 | +25% | 1 | 1 | 0% | 929 | 4,743 | +411% | 0 | 0 | — |
case-13 | pass→pass | 4,015 | 3,998 | -0% | 1 | 1 | 0% | 845 | 4,527 | +436% | 0 | 0 | — |
case-14 | pass→pass | 4,035 | 5,219 | +29% | 1 | 1 | 0% | 845 | 4,740 | +461% | 0 | 0 | — |
case-15 | pass→pass | 10,859 | 5,342 | -51% | 1 | 1 | 0% | 2,190 | 4,729 | +116% | 0 | 0 | — |
case-16 | pass→pass | 3,842 | 3,842 | 0% | 1 | 1 | 0% | 974 | 4,522 | +364% | 0 | 0 | — |
case-17 | pass→pass | 10,397 | 5,109 | -51% | 1 | 1 | 0% | 2,225 | 4,880 | +119% | 0 | 0 | — |
case-18 | pass→pass | 7,704 | 5,919 | -23% | 1 | 1 | 0% | 1,669 | 5,052 | +203% | 0 | 0 | — |
case-19 | fail→pass | 7,822 | 8,255 | +6% | 1 | 1 | 0% | 1,636 | 5,237 | +220% | 0 | 0 | — |
case-20 | pass→pass | 5,450 | 4,824 | -11% | 1 | 1 | 0% | 1,259 | 4,650 | +269% | 0 | 0 | — |
case-21 | pass→pass | 5,268 | 3,835 | -27% | 1 | 1 | 0% | 1,155 | 4,569 | +296% | 0 | 0 | — |
case-22 | pass→pass | 8,435 | 5,427 | -36% | 1 | 1 | 0% | 2,005 | 4,943 | +147% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +5 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.