lighting-environment
Dynamic lighting and environment in Decentraland scenes. LightSource, shadows, SkyboxTime, realm detection, and emissive materials. Use when the user wants lights, shadows, skybox control, day-night cycle, or glowing materials. Do NOT use for PBR material properties like metallic/roughness (see advanced-rendering).
How do I install this agent skill?
npx skills add https://github.com/decentraland/sdk-skills --skill lighting-environmentIs this agent skill safe to install?
- Gen Agent Trust Hubpass
The skill provides standard documentation and code snippets for implementing lighting effects, shadows, and environment controls in Decentraland. It uses official SDKs and refers to legitimate vendor repositories.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
What does this agent skill do?
Lighting and Environment in Decentraland
RULE: Use Smart Items for lights when the Creator Hub MCP is available
When the user asks to add a light and the Creator Hub MCP tools are present (mcp__creator-hub__*), always search_catalog first before manually creating an entity with LightSource. The Creator Hub catalog includes ready-made light Smart Items:
- Spotlight (
87c829b3-8e8d-4fcb-9f43-85d3aa9084f6, categorylights) -- comes with a real GLB model (assets/asset-packs/spotlight/spotlight.glb), a pre-configuredcore::LightSource, built-in Turn On / Turn Off / Toggle actions, and properinspector::Configfor the Creator Hub UI. - Point Light (category
lights) -- same pattern: GLB placeholder, pre-configuredcore::LightSourceof type Point, built-in actions. - There is also a decorative "Spotlight" (category
decorations) that is a visual-only fixture model with noLightSource-- make sure you pick from thelightscategory when the user wants actual illumination.
Use place_smart_item with the assetId and a position. It handles everything: downloading the GLB files, resolving asset paths, setting up asset-packs::Placeholder, asset-packs::Script, asset-packs::Actions, and inspector::Config components. The entity is immediately usable in the editor with action triggers wired up.
Only fall back to manual create_entity + set_component core::LightSource when:
- No Creator Hub MCP tools are available (pure code workflow).
- The user needs a light with no visual model (invisible light source).
- The user needs non-standard light parameters that a Smart Item does not expose.
The manual LightSource component API documented below remains the authoritative reference for both paths -- Smart Items use the same underlying component.
RULE: 3D model light fixtures do not emit light
Light-looking geometry in a GLB model (lamp meshes, bulb shapes, glowing filaments) is purely visual -- it does not cast actual dynamic light in Decentraland. The renderer treats it as regular geometry (possibly with an emissive material for a glow effect, but no illumination of surrounding objects). To get real dynamic lighting from a fixture model, attach a LightSource component to the same entity or to a child entity positioned at the light source. When a user has a scene model with built-in light fixtures, proactively mention this: "The light fixtures in your model are decorative geometry only -- I need to add LightSource components for actual illumination."
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
})
Defaults (from the protocol): active true, color white, intensity 16000 candela, range -1 (auto), shadow false. color is Color3 (RGB, each 0–1).
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). Default21.8. Min0, max179.outerAngle— outer fade angle (degrees). Default30. Max179.innerAnglecannot exceedouterAngle— if it does, the engine clamps them to the same value.- The light direction follows the entity's forward vector (set via Transform rotation).
typeis a discriminated union. To read/mutate spot params at runtime, narrow first:if (comp.type?.$case === 'spot') { comp.type.spot.innerAngle = 30 }
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 only rendered for spot lights, not point lights. shadow is a top-level boolean on the component (not inside Spot/Point).
Shadow Mask Textures (Gobos)
Project a pattern through the light:
const maskedLight = LightSource.getMutable(spotlight)
maskedLight.shadowMaskTexture = Material.Texture.Common({
src: 'assets/Images/lightmask1.png'
})
- Set
shadowMaskTexture = undefinedto remove the mask again. - The mask projects light shape (e.g. a window pattern) — simulating caustics/soft shadows. Used on spot lights.
Toggling Lights
// Toggle on/off
const lightData = LightSource.getMutable(light)
lightData.active = !lightData.active
Light Limits
- A scene may create many lights (the engine test scene spawns ~9 across 2 parcels); the renderer decides how many render.
- Depending on the player's quality settings, between ~4 and ~10 lights render at once. If the scene has more than that, only the closest lights to the player are rendered.
- Up to ~3 shadow-casting lights render at once.
- The renderer auto-culls lights based on quality settings and proximity.
- Intensity is in candela (lumens/m² at 1m, i.e. lumens/4π). Default
16000. rangedefault is-1→ auto-computed asintensity^0.25(fourth root, in meters). Set an explicitrangeto override — this also limits a light's influence and saves performance.- Spread lights out so few are near the player at once (only the closest ones render).
SkyboxTime (Day/Night Cycle)
Use SkyboxTime for atmosphere — nighttime scenes with point lights create dramatic environments.
Fixed Time in scene.json
Set a permanent time of day without code. Two valid locations:
// Genesis City scene — top-level
{ "skyboxConfig": { "fixedTime": 43200 } }
// World — inside worldConfiguration
{ "worldConfiguration": { "name": "my-name.dcl.eth", "skyboxConfig": { "fixedTime": 36000 } } }
Time values (seconds since midnight, full day = 86400): 0 = midnight, 21600 = 6 AM, 43200 = noon, 64800 = 6 PM (dusk), 86400 = full day.
Precedence (verified against the engine test scenes): worldConfiguration.skyboxConfig.fixedTime wins over top-level skyboxConfig.fixedTime; either JSON value is in turn overridden at runtime by a SkyboxTime component on engine.RootEntity.
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, TransitionMode } from '@dcl/sdk/ecs'
// Set time of day (must target the root entity)
SkyboxTime.create(engine.RootEntity, { fixedTime: 43200 }) // Noon
// Change with transition direction
SkyboxTime.createOrReplace(engine.RootEntity, {
fixedTime: 64800, // Dusk (6 PM)
transitionMode: TransitionMode.TM_BACKWARD // TM_FORWARD (0, default) or TM_BACKWARD (1)
})
// Remove the component to hand control back to global/world time
SkyboxTime.deleteFrom(engine.RootEntity)
transitionMode(optional) sets the animation direction when the time changes. DefaultTM_FORWARD.- The component must live on
engine.RootEntity. Deleting it reverts to the scene.json/world time (or the global day/night cycle).
Day/Night Cycle System
let currentTime = 43200
const CYCLE_SPEED = 100 // Time units per second
function dayNightCycle(dt: number) {
currentTime = (currentTime + CYCLE_SPEED * dt) % 86400
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 Quality
shadow is a top-level optional boolean on the LightSource component (default false). There is no shadow-type enum — quality is automatic and distance-based. Spot({...}) accepts only innerAngle? and outerAngle?.
import { LightSource } from '@dcl/sdk/ecs'
// Spot light with shadows enabled
LightSource.create(spotEntity, {
type: LightSource.Type.Spot({ innerAngle: 25, outerAngle: 45 }),
shadow: true, // top-level boolean
intensity: 800
})
Constraints:
- Shadows are only supported for spot lights; point lights do not cast shadows.
- Max 3 shadow-casting lights rendered at a time — disable
shadowon lights that don't need it. Spot lights with shadows suit dramatic effects such as flashlights. - Shadow quality/culling is automatic, based on the light's distance from the player. Exact distances vary by light type and the player's quality settings; general rule:
| Distance from player | Result |
|---|---|
| < 10 m | Soft shadows (high quality) |
| 10–20 m | Hard shadows (low quality) |
| > 20 m | No shadows rendered |
- The light itself keeps illuminating at much larger distances — it is only disabled when the player is more than 160 m away (10 parcels). This makes LightSource suitable for large-scale setups like stage lighting at live events, where most of the audience is far from the fixtures.
- Lights only render while the player is standing inside the scene; outside, they are not rendered.
Need advanced material effects? See the advanced-rendering skill for metallic, roughness, transparency, texture maps, texture tweens, and texture modes.
Platform Support
- Desktop (Unity explorer): Full support (point lights, spot lights, shadows).
- Mobile (Godot explorer):
LightSource(scene dynamic lights) is available since v1.13.0 — no longer a parity gap. Budgeted at runtime, see below. - Bevy explorer: Full support (point lights, spot lights, shadows).
Verified against docs commits b4d4c88 (mobile parity tracker) and 8139a7f (mobile limits), Oct 2026.
Mobile light budget
Mobile enforces flat scene limits on authored lights: 8 soft / 16 hard light sources, 2 soft / 4 hard shadow-casting lights. Soft = warning in the mobile performance panel; hard = the scene will not load.
On top of that, each mobile graphics profile only activates a budget of the nearest light sources — 0 on Very Low, up to 8 on High — and lower profiles may disable shadows entirely. So:
- Authoring 20 lights still costs the full scene budget even though at most 8 ever emit. Author to 8, do not lean on the runtime budget.
- Never make gameplay-critical visibility depend on a specific light being lit: on Very Low no scene light is active. Keep a baseline readable with ambient/emissive materials and the skybox, and treat
LightSourceas accent. - Shadows are not guaranteed. Do not use a cast shadow as the only cue for an object's position.
Full mobile limit table: optimize-scene → Mobile Scene Limits.
Gotchas
rangeleft unset (-1) is auto-derived from intensity asintensity^0.25— small intensities give surprisingly short range. Setrangeexplicitly for predictable falloff.shadowonly affects spot lights; setting it on a point light has no effect.- Animating a light's direction: put a
Tween/TweenSequence(Rotate mode) on the light entity — the beam follows the entity's forward vector. SkyboxTimeonRootEntityoverrides any scene.jsonfixedTime;deleteFromreverts to it.- Baked light geometry is inert. GLB models with lamp/bulb meshes do not emit light -- add a
LightSourcecomponent for real illumination. See the rule above. - Smart Item
Placeholderneeds a resolved file path, not a template variable. Setting{assetPath}/spotlight.glbas thesrcin anasset-packs::Placeholderresults in an invisible/broken gizmo.place_smart_itemresolves this automatically to e.g.assets/asset-packs/spotlight/spotlight.glb. If you must setPlaceholdermanually viaset_component, use the real on-disk path.
Example scenes
Engine-team test scenes (real API, exercised against the engine):
- https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/0,4-dynamic-lights — point & spot LightSource: toggle active, color, range, intensity, spot inner/outer angle at runtime, shadow-mask (gobo) swapping, and tweened light rotation.
- https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/2,0-skybox-scene-json — fixed skybox time via top-level
skyboxConfig.fixedTime; reads it back withgetSceneInformation. - https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/3,0-skybox-world-json — fixed skybox time via
worldConfiguration.skyboxConfig.fixedTime(World variant); demonstrates the worldConfiguration-wins precedence. - https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/2,1-skybox-sdk-scene-a and https://github.com/decentraland/sdk7-test-scenes/tree/main/scenes/3,1-skybox-sdk-scene-b — runtime
SkyboxTimeonRootEntitywithTransitionMode, plusdeleteFromto return to global time.
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/decentraland/sdk-skills/lighting-environment">View lighting-environment on skillZs</a>