Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
openaec-foundation avatar

Threejs Impl Shadows

  • 20 installs
  • 11 repo stars
  • Updated July 8, 2026
  • openaec-foundation/three.js-claude-skill-package

Helps with ai & agent building tasks.

About

threejs-impl-shadows is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.

  • threejs-impl-shadows
  • AI & Agent Building
  • AI-coding skill

Threejs Impl Shadows by the numbers

  • 20 all-time installs (skills.sh)
  • +2 installs in the week ending Aug 4, 2026 (Skillselion tracking)
  • Ranked #10,459 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
  • Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/openaec-foundation/three.js-claude-skill-package --skill threejs-impl-shadows

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs20
repo stars11
Last updatedJuly 8, 2026
Repositoryopenaec-foundation/three.js-claude-skill-package

What it does

Helps with ai & agent building tasks.

Files

SKILL.mdMarkdownGitHub ↗

threejs-impl-shadows

Quick Reference

Three-Step Shadow Opt-In

Shadows in Three.js require THREE explicit opt-in steps. Missing ANY step results in no shadows.

import * as THREE from 'three';

// Step 1: Enable shadow maps on the renderer
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;

// Step 2: Enable shadow casting on the light
light.castShadow = true;

// Step 3: Enable per-mesh shadow behavior
mesh.castShadow = true;       // this mesh casts shadows
ground.receiveShadow = true;  // this mesh receives shadows

Shadow Map Types

TypeConstantQualityCostSupports .radius
BasicTHREE.BasicShadowMapHard edges, aliasedLowestNo
PCFTHREE.PCFShadowMapSlightly softenedModerateNo
PCF SoftTHREE.PCFSoftShadowMapSoft penumbraHigherNo (ignores it)
VSMTHREE.VSMShadowMapGaussian blurModerateYes
  • Default in r160+: THREE.PCFShadowMap
  • PCFSoftShadowMap: best general-purpose choice; IGNORES the shadow.radius property
  • VSMShadowMap: supports shadow.radius and shadow.blurSamples but can exhibit light bleeding on thin geometry

Critical Warnings

NEVER forget any of the three opt-in steps -- missing even one produces zero shadows with no error message.

NEVER leave the DirectionalLight shadow camera frustum at default size -- it is almost ALWAYS wrong for your scene. ALWAYS configure it manually.

NEVER use more than 1-2 shadow-casting PointLights -- each PointLight shadow renders 6 cubemap faces per frame.

ALWAYS set shadow.mapSize to power-of-two values (512, 1024, 2048, 4096).

ALWAYS add light.target to the scene when repositioning a DirectionalLight or SpotLight target.

---

Shadow Map Types -- Detailed Comparison

BasicShadowMap

No filtering applied. Produces hard, aliased shadow edges. Use ONLY for debugging or stylized rendering where hard shadows are intentional.

PCFShadowMap (Default)

Percentage-Closer Filtering with a fixed-size kernel. Produces slightly softened edges. Good balance of quality and performance. The shadow.radius property has NO effect.

PCFSoftShadowMap

Uses a variable kernel for softer penumbra simulation. Higher quality than PCF but more expensive. The shadow.radius property is IGNORED -- softness is determined automatically by the filter.

VSMShadowMap

Variance Shadow Maps use a Gaussian blur pass. Supports shadow.radius (blur size) and shadow.blurSamples (sample count). Produces smooth soft shadows but suffers from light bleeding artifacts where thin geometry meets shadow receivers. NEVER use VSM for scenes with many thin overlapping shadow casters.

---

Per-Light Shadow Configuration

DirectionalLight Shadows

Uses an orthographic shadow camera. The frustum MUST be manually sized to encompass the shadowed area.

const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(5, 10, 7.5);
dirLight.castShadow = true;

// Shadow map resolution
dirLight.shadow.mapSize.width = 2048;
dirLight.shadow.mapSize.height = 2048;

// Orthographic frustum -- MUST size manually
dirLight.shadow.camera.near = 0.5;
dirLight.shadow.camera.far = 500;
dirLight.shadow.camera.left = -50;
dirLight.shadow.camera.right = 50;
dirLight.shadow.camera.top = 50;
dirLight.shadow.camera.bottom = -50;

// Anti-acne bias
dirLight.shadow.bias = -0.0001;
dirLight.shadow.normalBias = 0.02;

scene.add(dirLight);
scene.add(dirLight.target); // REQUIRED if repositioning target

Frustum sizing rule: Make the frustum as TIGHT as possible around the area that needs shadows. A frustum that is too large wastes shadow map resolution; a frustum that is too small clips shadows.

SpotLight Shadows

Uses a perspective shadow camera that auto-configures from the SpotLight .angle and .distance. Manual frustum configuration is typically unnecessary.

const spotLight = new THREE.SpotLight(0xffffff, 1);
spotLight.position.set(0, 10, 0);
spotLight.angle = Math.PI / 6;
spotLight.penumbra = 0.3;
spotLight.castShadow = true;

spotLight.shadow.mapSize.width = 1024;
spotLight.shadow.mapSize.height = 1024;
spotLight.shadow.bias = -0.0001;

scene.add(spotLight);
scene.add(spotLight.target);

SpotLightShadow exposes:

  • .focus (number, default 1) -- adjusts shadow camera FOV relative to spotlight FOV, range [0, 1]

PointLight Shadows

Uses a cubemap (6 perspective cameras, one per face). This is the MOST expensive shadow type -- rendering the scene 6 times per shadow-casting PointLight per frame.

const pointLight = new THREE.PointLight(0xffffff, 1, 100);
pointLight.position.set(0, 5, 0);
pointLight.castShadow = true;

// Lower resolution to offset the 6x render cost
pointLight.shadow.mapSize.width = 512;
pointLight.shadow.mapSize.height = 512;
pointLight.shadow.bias = -0.001;
pointLight.shadow.camera.near = 0.5;
pointLight.shadow.camera.far = 50;

scene.add(pointLight);

Performance rule: ALWAYS prefer SpotLight shadows over PointLight shadows. A PointLight shadow costs 6x what a SpotLight shadow costs. Use PointLight shadows ONLY when omnidirectional shadow casting is absolutely required.

---

Shadow Debugging with CameraHelper

ALWAYS use CameraHelper to visualize the shadow camera frustum when configuring shadows:

const shadowHelper = new THREE.CameraHelper(dirLight.shadow.camera);
scene.add(shadowHelper);

The helper renders the frustum as wireframe lines. If shadows are clipped, missing, or low resolution, the helper reveals whether the frustum is too small, too large, or misaligned.

ALWAYS remove CameraHelper in production builds.

---

Shadow Artifact Diagnosis Flowchart

Shadows not visible?
├── Check renderer.shadowMap.enabled === true
├── Check light.castShadow === true
├── Check mesh.castShadow / ground.receiveShadow === true
├── Check shadow camera frustum encompasses the scene (use CameraHelper)
└── Check shadow.camera.far is large enough

Striped lines on surfaces? (Shadow Acne)
├── Increase shadow.bias (start at -0.0001, go to -0.005)
├── Increase shadow.normalBias (0.02 to 0.1) for curved surfaces
└── Increase shadow.mapSize for more depth precision

Shadows float away from objects? (Peter Panning)
├── Reduce shadow.bias (you over-corrected for acne)
├── Use shadow.normalBias instead of shadow.bias
└── Increase shadow.mapSize resolution

Shadow edges shimmer when camera moves? (Shadow Swimming)
├── Increase shadow.mapSize resolution
├── Snap shadow camera to texel-aligned positions
└── Use Cascaded Shadow Maps (CSM) for large scenes

Light leaks through thin geometry? (Light Bleeding -- VSM only)
├── Switch from VSMShadowMap to PCFSoftShadowMap
├── Increase geometry thickness
└── Reduce shadow.radius value

Shadows on transparent/alpha-tested materials incorrect?
├── Set material.alphaTest threshold
├── Assign customDepthMaterial with matching alpha map
└── For fully transparent objects: use baked shadows or ContactShadows

---

Transparent Material Shadows

By default, shadows treat ALL geometry as fully opaque. For alpha-tested materials (e.g., tree leaves), assign a customDepthMaterial:

import * as THREE from 'three';

const alphaMap = new THREE.TextureLoader().load('leaf-alpha.png');

const leafMaterial = new THREE.MeshStandardMaterial({
  map: texture,
  alphaMap: alphaMap,
  alphaTest: 0.5,
  side: THREE.DoubleSide,
});

// Custom depth material for correct shadow casting
const depthMaterial = new THREE.MeshDepthMaterial({
  depthPacking: THREE.RGBADepthPacking,
  map: texture,
  alphaMap: alphaMap,
  alphaTest: 0.5,
});

leafMesh.material = leafMaterial;
leafMesh.customDepthMaterial = depthMaterial;
leafMesh.castShadow = true;

NEVER expect transparent objects (opacity < 1 without alphaTest) to cast correct real-time shadows. Use baked shadows or screen-space techniques instead.

---

Static Shadow Optimization

For scenes where lights and shadow casters do NOT move, disable automatic shadow updates:

renderer.shadowMap.autoUpdate = false;
renderer.shadowMap.needsUpdate = true; // render shadows once

Set renderer.shadowMap.needsUpdate = true ONLY when something changes. This eliminates per-frame shadow map rendering entirely for static scenes.

Per-light control:

light.shadow.autoUpdate = false;
light.shadow.needsUpdate = true; // update this light's shadow once

---

ContactShadows Alternative (Drei / React Three Fiber)

For ground-plane shadows, Drei's <ContactShadows> provides high-quality soft shadows at lower cost than shadow maps:

import { ContactShadows } from '@react-three/drei';

<ContactShadows
  position={[0, 0, 0]}
  opacity={0.5}
  scale={10}
  blur={1.5}
  far={1}
/>

Limitations: Works ONLY for ground-plane shadows. Does NOT project onto arbitrary geometry. Does NOT replace shadow maps for complex scenes.

---

Performance Budget

Shadow TypeCost per FrameMax Recommended
DirectionalLight shadow1 render pass1-2 lights
SpotLight shadow1 render pass2-4 lights
PointLight shadow6 render passes1 light max

Shadow map resolution impact: Doubling mapSize quadruples GPU memory usage. NEVER exceed 4096x4096 on mobile; prefer 2048x2048 or lower.

ALWAYS profile with renderer.info.render.calls to verify shadow pass count does not exceed your frame budget.

---

Reference Links

  • references/methods.md -- LightShadow API signatures and shadow map type constants
  • references/examples.md -- Complete shadow setup examples for each light type
  • references/anti-patterns.md -- Shadow artifacts, causes, and fixes

Official Sources

  • https://threejs.org/docs/#api/en/lights/shadows/LightShadow
  • https://threejs.org/docs/#api/en/lights/shadows/DirectionalLightShadow
  • https://threejs.org/docs/#api/en/lights/shadows/SpotLightShadow
  • https://threejs.org/docs/#api/en/lights/shadows/PointLightShadow
  • https://threejs.org/docs/#api/en/renderers/WebGLRenderer (shadowMap property)
  • https://threejs.org/docs/#api/en/helpers/CameraHelper

Related skills

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.