lighting-environment
Lighting and Environment in Decentraland
Point Lights
Emit light in all directions from a position:
import { engine, Transform, LightSource } from '@dcl/sdk/ecs'
import { Vector3, Color3 } from '@dcl/sdk/math'
const light = engine.addEntity()
Transform.create(light, { position: Vector3.create(8, 3, 8) })
LightSource.create(light, {
type: LightSource.Type.Point({}),
color: Color3.White(),
intensity: 16000 // candela
})
Colored Point Light
LightSource.create(light, {
type: LightSource.Type.Point({}),
color: Color3.create(1, 0.5, 0), // Warm orange
intensity: 16000,
range: 15 // Maximum distance in meters
})
Spot Lights
Emit a cone of light in a direction:
import { Quaternion } from '@dcl/sdk/math'
const spotlight = engine.addEntity()
Transform.create(spotlight, {
position: Vector3.create(8, 4, 8),
rotation: Quaternion.fromEulerDegrees(-90, 0, 0) // Point downward
})
LightSource.create(spotlight, {
type: LightSource.Type.Spot({ innerAngle: 25, outerAngle: 45 }),
color: Color3.White(),
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: 800
})
Note: shadows are not available on point lights, only on spoit 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 in the scene (16m x 16m). Scenes with multiple parcels can group lights close together.
- The renderer auto-culls lights based on quality settings and proximity
- Up to ~3 shadowed lights visible at once
- Intensity is in candela — visible distance grows roughly with
sqrt(intensity) - Depending on the player's quality settings, they may see as much as 10 lights rendered at the same time, or as little as 4. If the scene is trying to render more lights than this, only the closest ones to the player will be rendered.
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)
For a visual glow without casting light on surroundings:
import { engine, Material } from '@dcl/sdk/ecs'
import { Color4, Color3 } from '@dcl/sdk/math'
// Self-illuminated material (emissiveColor uses Color3, not Color4)
Material.setPbrMaterial(entity, {
albedoColor: Color4.create(0, 0, 0, 1),
emissiveColor: Color3.create(0, 1, 0), // Green glow
emissiveIntensity: 2.0
})
Note: emissive materials don't illuminate other surrounding entities, they just have a glow effect on them.
Combining Emissive + LightSource
For an object that both glows visually and casts light:
// Visual glow on the mesh
Material.setPbrMaterial(bulb, {
emissiveColor: Color3.create(1, 0.9, 0.7),
emissiveIntensity: 1.5
})
// Actual light emission
LightSource.create(bulb, {
type: LightSource.Type.Point({}),
color: Color3.create(1, 0.9, 0.7),
intensity: 200,
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: 800
})
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, texture tweens, and texture modes.
Best Practices
- Stay within the one light per parcel budget
- 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 sadow-casting lights 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
More from decentraland/sdk-skills
decentraland-sdk-skills
Build, extend, and deploy Decentraland SDK7 scenes. Contains agent behavioral guidelines, the composite-first rule, and an index of all topic skills with reference files. Install with a single command — no flags needed.
22build-ui
Build 2D screen-space UI for Decentraland scenes using React-ECS (JSX). Create HUDs, menus, health bars, scoreboards, dialogs, buttons, inputs, and dropdowns. Use when the user wants screen overlays, on-screen UI, HUD elements, menus, or form inputs. Do NOT use for 3D in-world text (see advanced-rendering) or clickable 3D objects (see add-interactivity).
3advanced-input
System-level input polling and player movement control in Decentraland. Covers inputSystem (isTriggered/isPressed for held keys, WASD polling), InputModifier (freeze/restrict player movement), PointerLock (cursor capture detection), PrimaryPointerInfo (cursor screen coords and world ray), and number-key action bar patterns. Use when the user wants continuous key polling, WASD-controlled entities, to freeze the player during a cutscene, FPS-style cursor lock, or multi-key combo patterns. For event-driven clicks and hover on entities see add-interactivity.
3nft-blockchain
NFT display and blockchain interaction in Decentraland. NftShape (framed NFT artwork), wallet checks (getPlayer, isGuest), signedFetch (authenticated requests), smart contract interaction (eth-connect, createEthereumProvider), and RPC calls. Use when the user wants NFTs, blockchain, wallet, smart contracts, Web3, crypto, or token gating. Do NOT use for player avatar data or emotes (see player-avatar).
3player-physics
Apply physics forces to the player in Decentraland scenes. Impulses (one-shot pushes), knockback (push away from a point with falloff), continuous forces (wind tunnels), timed forces, and repulsion fields. Use when the user wants launch pads, knockback on hit, wind zones, gravity fields, or any scene-applied force on the player. Do NOT use for player movement speed (see player-avatar) or platform movement (see animations-tweens).
3camera-control
Control camera behavior in Decentraland scenes. CameraMode detection (first/third person, onChange listener), CameraModeArea (force a mode inside a box), VirtualCamera (cinematic scripted cameras with Speed/Time transitions and lookAtEntity), MainCamera (activate/deactivate virtual cameras), and camera vs collider interactions (CL_PHYSICS + CL_POINTER). Use when the user wants camera control, cutscenes, cinematic views, forced camera modes, or camera tracking. Do NOT use for input restriction during cutscenes (see advanced-input for InputModifier) or cursor lock detection (see advanced-input for PointerLock).
3