TimeOfDay is an environment object that drives the level day/night cycle.
It calculates seasonal sun elevation and azimuth from normalized time, latitude, date, and axisTilt, then broadcasts updates to environment systems. The current astronomical sky also reads this object as the level’s single source of clock, latitude, longitude, date, and UTC offset.
TimeOfDay is commonly used with advanced lighting and a compatible sky object, such as ScatterSky. The core_celestial runtime reads TimeOfDay just before rendering and pushes the live sun, moon, stars, meteors, and sky profile data into ScatterSky.
TimeOfDay only has a visible lighting effect when other environment objects listen to it or use its sun/time data. By itself, it is a global time controller, not a sky renderer.For sky authoring, see Sky and Weather , Sky, Atmosphere, and Night Sky , and Night Lighting .
For most levels, author startTime, play, dayLength, latitude, longitude, year, month, day, and utcOffset. Use celestialProfile only when the level needs a custom sky profile.
{
"class": "TimeOfDay",
"name": "tod",
"axisTilt": 23.44,
"dayLength": 1800,
"startTime": 0,
"time": 0,
"play": false,
"latitude": 47,
"longitude": 8,
"year": 2026,
"month": 6,
"day": 20,
"utcOffset": "2",
"celestialProfile": "earth",
"position": [0, 0, 0],
"rotationMatrix": [1,0,0,0,1,0,0,0,1],
"scale": [1, 1, 1]
}
| Field | Type | Description |
|---|---|---|
class |
string | Must be "TimeOfDay". |
name |
string | Scene object name, commonly "tod". |
axisTilt |
number | Sun path tilt in degrees for TimeOfDay’s engine sun calculation. The current celestial runtime primarily uses time, latitude, longitude, date, and UTC offset for real sky placement. |
dayLength |
number | Length of a full virtual day in real-world seconds. |
startTime |
number | Time used when the level starts. |
time |
number | Current normalized local day time. Hidden in inspectors. |
play |
bool | Whether time advances automatically. |
latitude |
number | Observer latitude in degrees north. Used by the engine sun and astronomical sky. |
longitude |
number | Observer longitude in degrees east. Used by the astronomical sky and civil time conversion. |
year |
number | UTC calendar year used for moon, stars, and meteor shower positions. |
month |
number | UTC calendar month, 1-12. |
day |
number | UTC calendar day of month, 1-31. |
utcOffset |
string/number | Optional dynamic field for exact civil UTC offset in hours, including DST. |
dstRule |
string | Optional dynamic field: auto, eu, us, au, or none. |
celestialProfile |
string | Optional dynamic field selecting a sky profile by bare name or path. |
TimeOfDay uses normalized local civil day time. In the current sky path, 0 is local noon:
0.00 -> 12:00 / local noon
0.25 -> 18:00 / evening
0.50 -> 00:00 / midnight
0.75 -> 06:00 / morning
1.0 -> wraps to 0.0
When assigned through setTimeOfDay, time is wrapped into the 0..1 range:
mTimeOfDay = time - floor(time);
Examples:
| Input | Stored time |
|---|---|
0.15 |
0.15 |
1.15 |
0.15 |
-0.1 |
0.9 |
A common fixed noon setup is:
"startTime": 0
The exact visual result depends on latitude, longitude, date, UTC offset, sky setup, and terrain orientation.
"startTime": 0
startTime is copied into the current time when the object is added.
"time": 0.15
time is the current normalized local day time.
It is a protected field and hidden in inspectors. Setting it calls setTimeOfDay(), which wraps it and immediately updates sun position.
For level files, prefer startTime over time, because onAdd() resets time from startTime.
"play": true
When play is true, the time advances every simulation tick.
The update uses:
deltaTime = tickSeconds * simulationTimeScale
time += deltaTime / dayLength
If play is false, the time remains fixed unless changed by script/editor or animation.
"dayLength": 1800
dayLength is the length of one full virtual day in real-world seconds.
Examples:
dayLength |
Meaning |
|---|---|
600 |
10-minute day |
1800 |
30-minute day |
3600 |
1-hour day |
86400 |
Real-time 24-hour day |
Use longer values for slow natural progression and shorter values for visible day/night cycles.
TimeOfDay calculates:
These values are broadcast through TimeOfDay::smTimeOfDayUpdateSignal.
"axisTilt": 23.44
axisTilt is the angle in degrees between the global equator and tropic. It controls the amplitude of seasonal solar declination in TimeOfDay’s engine sun calculation.
The default Earth-like value is:
23.44
Together with latitude and date, it changes sun height and day length through the year for systems that use the TimeOfDay sun values. The current core_celestial sky path reads the same clock, latitude, longitude, date, and UTC offset data and pushes the visible sun and night sky into ScatterSky.
"latitude": 45.0,
"longitude": 9.0
latitude and longitude define the observer location for the current sky systems.
latitude affects the engine sun path and astronomical sky.longitude affects astronomical sky orientation and local civil time conversion.Use approximate real-world coordinates if the level represents a real place. For fictional levels, choose values that give the desired sun path and night sky orientation. Do not leave longitude at 0 unless that is intentional; it changes the apparent sky and derived timezone behavior.
"year": 2026,
"month": 6,
"day": 23
The date is used by the astronomical sky for moon position, moon phase, star orientation, and meteor shower state.
It is also used by TimeOfDay to calculate seasonal solar declination.
TimeOfDay, latitude, longitude, date, and UTC offset together."utcOffset": "2",
"dstRule": "eu"
utcOffset and dstRule are optional dynamic fields used by core_celestial.
utcOffset pins the exact civil UTC offset in hours, including daylight saving time.dstRule can be auto, eu, us, au, or none.Set utcOffset when the level should match a specific real-world local time. Longitude and UTC offset are related but not the same: longitude positions the observer on Earth, while utcOffset says how the level’s local civil clock maps to UTC.
"celestialProfile": "earth"
celestialProfile is an optional dynamic field used by core_celestial.
Bare names resolve to:
/art/skies/profiles/<name>.json
For example, earth resolves to /art/skies/profiles/earth.json. You can also set a direct JSON path, or add a celestial.json file next to the level’s mission file.
TimeOfDay initializes an internal sun color curve.
The curve maps normalized elevation to colors for:
The built-in color targets range from:
0 radians -> high noon
PI radians -> midnight
The default curve gradually transitions from white daylight to warm dawn/dusk colors and darker bluish night colors.
These targets are internal in this implementation and not exposed as persistent fields in the shown code.
When a TimeOfDay object is added:
time from startTime.onAdd script callback.When removed, it is removed from the scene.
TimeOfDay supports animated transition toward a target angle.
The method:
animate(f32 time, f32 speed)
takes:
| Parameter | Meaning |
|---|---|
time |
Target time angle in degrees, clamped to 0..360. |
speed |
Animation speed in degrees per second. |
Internally, current normalized time is converted to degrees:
currentDegrees = timeOfDay * 360
If the target is behind the current time, it wraps forward by 360 degrees so animation always proceeds forward through the day.
Example concept:
current = 90 degrees
target = 45 degrees
actual target = 405 degrees
The animation stops when it reaches the target.
{
"class": "TimeOfDay",
"name": "tod",
"axisTilt": 23.44,
"dayLength": 1800,
"startTime": 0,
"time": 0,
"play": false,
"latitude": 47,
"longitude": 8,
"year": 2026,
"month": 6,
"day": 20,
"utcOffset": "2"
}
Use this for a fixed local noon setup.
{
"class": "TimeOfDay",
"name": "tod",
"axisTilt": 23.44,
"dayLength": 2400,
"startTime": 0.75,
"time": 0.75,
"play": true,
"latitude": 47,
"longitude": 8,
"year": 2026,
"month": 6,
"day": 20,
"utcOffset": "2"
}
This creates a 40-minute full cycle starting around local morning.
{
"class": "TimeOfDay",
"name": "tod",
"axisTilt": 23.44,
"dayLength": 1800,
"startTime": 0,
"time": 0,
"play": false,
"latitude": 0,
"longitude": 0,
"year": 2026,
"month": 6,
"day": 20,
"celestialProfile": "earth"
}
Use this pattern when a level should load a named Earth-based sky profile from /art/skies/profiles/. Most levels can omit celestialProfile and use the built-in Earth defaults.
{
"class": "TimeOfDay",
"name": "tod_preview",
"axisTilt": 23.44,
"dayLength": 120,
"startTime": 0,
"time": 0,
"play": true
}
Useful for quickly checking lighting transitions.
TimeOfDay does not render the sky or sun by itself.
It is intended to work with environment systems that read or listen to its time data, such as:
ScatterSkycore_celestialTimeOfDay::smTimeOfDayUpdateSignalcore_celestial is enabled.TimeOfDay object per level.startTime for the level’s initial time.time and startTime consistent in saved files for clarity.play for fixed-lighting maps.play only when the level should have a moving day/night cycle.dayLength values unless you intentionally want fast cycling.ScatterSky.latitude, longitude, date, and UTC offset when the real sky, moon, and meteor showers should matter.celestialProfile or level-local celestial.json for custom sky presets.Possible causes:
core_celestial is disabled or not loaded.play is false and the time is not being changed.Check:
"startTime": 0
Remember that onAdd() initializes current time from startTime.
Also remember that the current convention uses 0 as local noon, 0.25 as evening, 0.5 as midnight, and 0.75 as morning.
Increase:
"dayLength": 3600
Also check the simulation time scale.
Check:
"latitude": 47,
"longitude": 8,
"year": 2026,
"month": 6,
"day": 20
Also check axisTilt, longitude, and utcOffset. The current sky calculates a seasonal sun path instead of using a fixed azimuth override.
TimeOfDay is the level day/night controller.
It stores normalized local day time, advances it when play is enabled, calculates seasonal sun elevation and azimuth, and provides latitude, longitude, date, UTC offset, and profile data to the astronomical sky. Use it with a compatible sky object such as ScatterSky for visible day/night lighting changes.
Was this article helpful?