About lighting-environment
A reference skill for Decentraland scene authors who need to configure lighting, emissive materials, and time of day. It covers both scene declarations and runtime changes, with practical notes on light and shadow limits.
Key features
- 01Declare point and spot lights
- 02Configure shadows and mask textures
- 03Toggle lights at runtime
- 04Set fixed or changing skybox time
- 05Combine emissive materials with lights
Use cases
- 01Add fixture lighting to a Decentraland scene
- 02Create colored or directional stage lighting
- 03Build a day-night cycle
- 04Adjust lights while a scene is running
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ dcl-regenesislabs/ opendcl --skill lighting-environmentAfter installing, type/lighting-environmentin Claude Code to confirm it loaded.
Details
From the project READMELighting and Environment in Decentraland
Authoring split
LightSource is supported in main-entities.ts — declare the lamp's fixture model AND its light in the same entry. The user can drag the fixture in the editor and the light follows.
// main-entities.ts
import type { Scene } from '@dcl/sdk/scene-types'
export const scene = {
ceiling_lamp: {
components: {
Transform: { position: { x: 8, y: 3, z: 8 } },
GltfContainer: { src: 'models/lamp.glb' },
LightSource: {
type: { $case: 'point', point: {} },
color: { r: 1, g: 1, b: 1 },
intensity: 16000
}
}
}
} satisfies Scene
For purely decorative lights with no mesh, declare a Transform-only entity and skip GltfContainer. Toggling lights at runtime still happens in src/index.ts via LightSource.getMutable(getEntityOrNullByName('ceiling_lamp')).
Point Lights
Emit light in all directions from a position:
// main-entities.ts entry
ambient_point: {
components: {
Transform: { position: { x: 8, y: 3, z: 8 } },
LightSource: {
type: { $case: 'point', point: {} },
color: { r: 1, g: 1, b: 1 },
intensity: 16000 // candela — point lights typically need 8000–32000 to be visible
}
}
}
Colored Point Light
LightSource.create(light, {
type: LightSource.Type.Point({}),
color: Color3.create(1, 0.5, 0), // Warm orange
intensity: 12000,
range: 15 // Maximum distance in meters
})
Spot Lights
Emit a cone of light in a direction. The cone's orientation comes from the entity's Transform rotation.
// main-entities.ts
stage_spot: {
components: {
Transform: {
position: { x: 8, y: 4, z: 8 },
rotation: { x: -0.7071, y: 0, z: 0, w: 0.7071 } // pitch -90° (down)
},
GltfContainer: { src: 'models/spotlight.glb' },
LightSource: {
type: { $case: 'spot', spot: { innerAngle: 25, outerAngle: 45 } },
color: { r: 1, g: 1, b: 1 },
intensity: 16000
}
}
}
innerAngle— full-brightness cone angle (degrees)outerAngle— outer fade angle (degrees)- The light direction follows the entity's forward vector (set via Transform rotation)
Shadows
Enable shadows on point or spot lights:
LightSource.create(spotlight, {
type: LightSource.Type.Spot({ innerAngle: 25, outerAngle: 45 }),
shadow: true,
intensity: 16000
})
Shadows are only available on spot lights, not point lights.
Shadow Mask Textures (Gobos)
Project a pattern through the light:
const maskedLight = LightSource.getMutable(spotlight)
maskedLight.shadowMaskTexture = Material.Texture.Common({
src: 'assets/scene/images/lightmask1.png'
})
Toggling Lights
// Toggle on/off
const lightData = LightSource.getMutable(light)
lightData.active = !lightData.active
Light Limits
- Maximum one active light per parcel (16m x 16m) — multi-parcel scenes can group lights close together when needed.
- The renderer auto-culls lights based on quality settings and proximity. Quality range allows 4–10 lights visible simultaneously.
- Shadows are only available on spot lights; up to ~3 shadowed lights visible at once.
- Intensity is in candela. Practical visible range: point lights ~8000–32000, spot lights ~10000–24000. Values below ~1000 are usually invisible.
- Emissive materials don't illuminate surrounding entities — they only have a glow effect on themselves. Combine an emissive material with a
LightSourcefor both.
SkyboxTime (Day/Night Cycle)
Fixed Time in scene.json
Set a permanent time of day without code:
{
"skyboxConfig": {
"fixedTime": 36000
}
}
Time values: 0 = midnight, 36000 = noon, 54000 = dusk, 72000 = midnight again.
Read Current World Time
import { getWorldTime } from '~system/Runtime'
executeTask(async () => {
const time = await getWorldTime({})
console.log('Seconds since midnight:', time.seconds)
})
Change Time Dynamically
import { engine, SkyboxTime } from '@dcl/sdk/ecs'
// Set time of day (must target root entity)
SkyboxTime.create(engine.RootEntity, { fixedTime: 36000 }) // Noon
// Change with transition direction (TransitionMode from the generated protobuf)
SkyboxTime.createOrReplace(engine.RootEntity, {
fixedTime: 54000, // Dusk
transitionMode: 1 // TM_BACKWARD
})
Day/Night Cycle System
let currentTime = 36000
const CYCLE_SPEED = 100 // Time units per second
function dayNightCycle(dt: number) {
currentTime = (currentTime + CYCLE_SPEED * dt) % 72000
SkyboxTime.createOrReplace(engine.RootEntity, {
fixedTime: currentTime
})
}
engine.addSystem(dayNightCycle)
Realm Info
Detect which realm (server) the player is connected to:
import { getRealm } from '~system/Runtime'
executeTask(async () => {
const realm = await getRealm({})
console.log('Realm:', realm.realmInfo?.realmName)
console.log('Network:', realm.realmInfo?.networkId)
console.log('Base URL:', realm.realmInfo?.baseUrl)
})
Emissive Materials (Glow Effects)
Material is supported in main-entities.ts, so a glow that doesn't need to cast light on surroundings can be declared inline:
// main-entities.ts
glowing_orb: {
components: {
Transform: { position: { x: 8, y: 1, z: 8 } },
MeshRenderer: { mesh: { $case: 'sphere', sphere: {} } },
Material: {
material: {
$case: 'pbr',
pbr: {
albedoColor: { r: 0, g: 0, b: 0, a: 1 },
emissiveColor: { r: 0, g: 1, b: 0 },
emissiveIntensity: 2.0
}
}
}
}
}
Combining Emissive + LightSource
A single entity can carry both — emissive glow on the material AND light emission.
// main-entities.ts
bulb: {
components: {
Transform: { position: { x: 8, y: 3, z: 8 } },
GltfContainer: { src: 'models/bulb.glb' },
Material: {
material: {
$case: 'pbr',
pbr: {
emissiveColor: { r: 1, g: 0.9, b: 0.7 },
emissiveIntensity: 1.5
}
}
},
LightSource: {
type: { $case: 'point', point: {} },
color: { r: 1, g: 0.9, b: 0.7 },
intensity: 12000,
range: 10
}
}
}
Shadow Types
Control shadow quality per light. Shadow type is set inside the Spot() or Point() helper:
import { LightSource, PBLightSource_ShadowType } from '@dcl/sdk/ecs'
// Spot light with soft shadows
LightSource.create(spotEntity, {
type: LightSource.Type.Spot({
innerAngle: 25,
outerAngle: 45,
shadow: PBLightSource_ShadowType.ST_SOFT
}),
shadow: true,
intensity: 16000
})
Available shadow types:
PBLightSource_ShadowType.ST_NONE— no shadows (cheapest)PBLightSource_ShadowType.ST_HARD— crisp shadows (medium cost)PBLightSource_ShadowType.ST_SOFT— smooth, blurred shadows (most expensive)
Need advanced material effects? See the advanced-rendering skill for metallic, roughness, transparency, texture maps, and texture modes.
Best Practices
- Stay within the one light per parcel budget — plan light placement around scene parcels
- Use emissive materials for decorative glow that doesn't need to illuminate surroundings
- Combine emissive materials with LightSource for realistic light fixtures (lamp = emissive mesh + point light)
- Use spot lights with shadows for dramatic effects (stage lighting, flashlights)
- Keep shadow count low (max ~3 visible) — disable
shadowon lights that don't need it - Set
rangeon lights to limit their influence and save performance - Use
SkyboxTimefor atmosphere — nighttime scenes with point lights create dramatic environments