DayNightCycle
Game time that moves the sun, so an afternoon drive turns into a night drive.
What it does
DayNightCycle.core turns an hour into a lighting state: where the key light comes from, its colour and strength, the sky fill, the haze colour and how bright reflections are. It also gives one number for everything else, darkness, which is 0 at noon and 1 at midnight. Headlights and street lamps key off it.
The key light is the sun by day and the moon by night. Both use the same directional light, and the swap happens when both are at their dimmest, so the shadows never jump.
Use it in a scene
A scene is just data — a list of entities, each with its components. At startup the SceneLoader turns this JSON into a live world; edit the file and reload to rebuild it.
Build it from scratch with the bjs CLI:
- 1Scaffold a project
World, renderer, and dev server — ready to run.
- 2Install dependencies
- 3Add DayNightCycle
Copies its source into src/ so the scene resolves.
First time? Run bjs login once.
- 4Paste the scene into src/scenes/arcade-room.ts and run
SceneLoader builds the world from the JSON; reload to rebuild.
It creates no lights. It picks up the handles the scene's lights announce and drives them every frame. Call registerDayNightCycleExtension(adapter) at bootstrap. Without it the clock still lights the scene, but reflections stay at daytime strength and skies does nothing.
Darkness from the hour
The sun's height is a sine wave that crosses the horizon at 6 and 18. Darkness stays at 0 until the sun is low, then climbs fast. At sunset it is 0.63, and about half an hour later it reaches 1. The whole dusk takes about 76 game minutes, which is under half a real minute at the default hoursPerMinute. With the default lightsOnAt, dayNight.nightfall fires at about 17:50.
HDR skies
Give it skies and it lights the scene with real sky photos (HDRIs) as the day turns:
A scene is just data — a list of entities, each with its components. At startup the SceneLoader turns this JSON into a live world; edit the file and reload to rebuild it.
Build it from scratch with the bjs CLI:
- 1Scaffold a project
World, renderer, and dev server — ready to run.
- 2Install dependencies
- 3Add DayNightCycle
Copies its source into src/ so the scene resolves.
First time? Run bjs login once.
- 4Paste the scene into src/scenes/arcade-room.ts and run
SceneLoader builds the world from the JSON; reload to rebuild.
urlis a prefiltered.envsky. Make one from an.hdrwith Babylon'sEnvironmentTextureTools.CreateEnvTextureAsync.sunHeightis the sine of the sun's angle above the horizon when the photo was taken. The clock shows the sky shot nearest the current sun height.sunUis where the sun sits across the photo (0 at the left edge, 1 at the right). The sky is turned every frame so its sun lines up with the light casting the shadows. The glint on a car roof then agrees with its shadow.levelevens out skies shot at different exposures.
All skies start loading at once, and a sky still loading is never put up, so a swap never flashes. With glossy, physically based materials the sky does most of the fill lighting, so keep the HemisphericLight low. The skies in the Endless Rampage scene are Poly Haven's CC0 "Qwantani" set by Greg Zaal and Jarod Guest.
If the scene also has an EnvironmentTexture, the DayNightCycle sky takes over as soon as its photo loads. EnvironmentTexture then only covers the first moments, and engines without the DayNight plugin.
Shadow box
A directional light's shadow covers a box centred on the light. Leave the light at the origin and a car three kilometres down the road has no shadow. So every frame the sun is moved to sit shadowDistance back up its own ray from the follow entity, a little ahead of the car.
Settings
| Field | Default | What it does |
|---|---|---|
hour | 15 | Starting hour, 0 to 24. |
hoursPerMinute | 3 | Game hours per real minute. 0 stops the clock. |
sun | "Sun" | Entity carrying the DirectionalLight this clock drives. |
ambient | "Ambient" | Entity carrying the HemisphericLight this clock drives. |
follow | "Player" | Entity the sun's shadow box is kept centred on. |
shadowDistance | 90 | How far up the light's ray the sun sits from follow. |
lightsOnAt | 0.42 | Darkness at which dayNight.nightfall fires (headlights on). |
fog | true | Drive the scene fog too. |
fogDensityDay | 0.0038 | Fog density at noon. |
fogDensityNight | 0.0075 | Fog density at midnight. |
sunIntensity | 1.35 | Sun strength when it is high. |
moonIntensity | 0.2 | Moonlight strength at midnight. |
ambientDay | 0.62 | Sky fill at noon. |
ambientNight | 0.1 | Sky fill at midnight. |
environmentDay | 0.9 | Reflection strength at noon. |
environmentNight | 0.06 | Reflection strength at midnight. |
sunAzimuth | 0.6 | Compass bearing the sun rises from, radians (0 = +X). |
skies | [] | HDR sky photos to light the scene with. Empty keeps the scene's own sky and only dims it. |
Events
| Event | Payload | Direction |
|---|---|---|
dayNight.changed | { entityId, hour, darkness, phase } | emits when darkness moves by 0.01, and at least every 0.25 s. phase is day, dusk, night or dawn |
dayNight.nightfall | { entityId, hour } | emits when darkness rises past lightsOnAt |
dayNight.daybreak | { entityId, hour } | emits when darkness falls back below lightsOnAt |
dayNight.time.set | { hour } | listens, jumps the clock |
dayNight.speed.set | { hoursPerMinute } | listens, changes how fast the day goes |
directionallight.created | { entityId, handle } | listens, picks up the sun light |
hemisphericlight.created | { entityId, handle } | listens, picks up the ambient light |
Needs
- DirectionalLight: plays both the sun and the moon.
- HemisphericLight: soft fill from above.
- MeshPrimitive: tells it where the car is.




















































