---
title: "Day night cycle and moving sun by script"
description: "The sun arcs overhead and the scene warms into sunset then dark. The sky preset, fog, and tone mapping are a static world look (see atmosphere-sky-and-lighting); a MOVING cycle is a script that patches a `LightComponent` over time. The `TimerComponent` contract itself names day/night cycles as a use case."
engineVersion: v1.0.234
date: 2026-09-28
license: "(c) Gessa, proprietary. Cite with attribution to https://gessa.ai/docs/. Terms: https://gessa.ai/terms/."
canonical: https://gessa.ai/docs/knowledge/playbooks/day-night-cycle-scripting/
---

# Day night cycle and moving sun by script

## When to use

The sun arcs overhead and the scene warms into sunset then dark. The sky preset,
fog, and tone mapping are a static world look (see atmosphere-sky-and-lighting);
a MOVING cycle is a script that patches a `LightComponent` over time. The
`TimerComponent` contract itself names day/night cycles as a use case.

## The recipe

Author with `project_apply_script_semantic_patch` using the `ai_safe` op pack.
The sun is an entity with a directional `LightComponent` (`mode:"directional"`).

1. Advance a clock: keep a `timeOfDay` phase (0 to 1) in durable state. Drive it
   from `addTickHandler` scaled by `ctx.delta`, or from a `TimerComponent` plus
   `addTimerHandler` for coarse steps. `incrementNumericState`
   `{ "field":"timeOfDay", "by": <delta / dayLengthSeconds> }` and wrap at 1.
2. Move the sun: in the body (whole-body GessaScript via
   `replaceScriptFromGessaScript`), map `timeOfDay` to a sun angle and
   `ctx.entity.patchComponent` the light's `rotation` (a directional light's
   direction follows its transform). This is the moving-sun arc.
3. Change the look: patch `intensity` down toward night and back up toward day,
   and patch `color` warm at dawn and dusk, cool at noon, dark at night. Turn
   `castShadow` on for a hard midday sun if desired.
4. No script needed for oscillating light: set `LightComponent.modulation`
   (`mode` one of `none`, `strobe`, `pulse`, `flicker`, `disco`, plus `rateHz`
   and `depth`) for a torch flicker or a club strobe directly on the component.
5. Reuse the same phase to drive ambient fill: patch a second `ambient`-mode
   light's `intensity` so shadows do not go pure black at night.

## Pitfalls

- Scale the phase step by `ctx.delta`; a fixed per-tick step makes day length
  depend on frame rate.
- Store `timeOfDay` in durable state so a reload resumes at the same time and all
  clients read one clock.
- There is NO per-field AI tool to set the world sky's built-in time-of-day
  (`WorldAuthoredEnvState.simTimeSeconds`/`timeOfDayRate`); animate an authored
  `LightComponent` yourself instead of expecting a sky-clock setter.
- Keep `intensity` at or above a small floor; a directional light at 0 with no
  ambient fill renders a black scene.
- Prefer `LightComponent.modulation` over a per-tick script for pure flicker or
  strobe; it is cheaper and deterministic.

## Verify

- `simulation_run` (`qa.run.start`) to watch a full cycle move the sun and shift
  intensity and color from day to night.
- `project_get_graph_snapshot` to confirm the tick or timer handler and the
  directional `LightComponent` are present.
