Skip to content

Timelines and the SceneClock

Deepratna Awale edited this page Oct 6, 2026 · 5 revisions

Timelines and the SceneClock

How scene time works in Open Wallpaper Engine: one clock per wallpaper instance, and Wallpaper Engine's timeline animations evaluated on it. The evidence and plan are in docs/timeline-plan.md.

The SceneClock

Every wallpaper instance has its own SceneClock: seconds since its scene loaded, as a double. Every consumer of scene time reads it: keyframe animations, g_Time, particles, scripts, camera shake and parallax. Audio smoothing takes its rate.

It advances like WE's main loop:

  1. The frame's wall time is clamped to 0.0001…0.25 s.
  2. It is multiplied by the playback rate (WE's rate ÷ 100, at least 0.1; OWE's Animation Speed property) and clamped again.
  3. The clock wraps back to 0 after 432 000 s (five days).

A rate change affects only the next step, so animations never jump.

Pausing

A pause eases the rate to 0, as WE does (a fraction of the gap each frame), and the displays stop drawing only once it has reached 0. This applies to the app's Pause and to the playback rules pausing every display a wallpaper shows on. A single display paused while others play the same instance freezes at once, since its frames are the instance's.

The Animation Speed is read from the instance's own property store (ScenePlaybackSpeed). Sound layers' timers take the scene step.

Timelines

SceneAnimationSet holds every property timeline of one instance, keyed by site (owner and property):

  • Sites: a layer's fields, its particle overrides, its effects' fields, their materials' constants, and the scene's general settings.
  • Each animation's clock owner is its parent, else itself; the owner's clock advances once per frame by delta × rate, firing the events it crosses.
  • Each animation samples its own channels on the owner's clock every frame.
  • WE's format: Bézier handles, per-frame samples, single / loop / mirror modes, startpaused, wraploop, relative, linked clocks.

Priority: the timeline beats the static and user value; a script's return wins for its frame. Animated visible stays undrawn, as in WE.

The maths match a float32 reference model of WE's evaluation bit for bit (Scripts/timeline-reference.py).

Texture animations

Sprite sheets (TEXS frames, on layers, effects and materials) have one clock per texture, one step per frame, with the script overrides rate, pause, stop, setFrame and join (SceneTextureAnimations).

Script API

IAnimation on objects, effects, materials and the scene; thisScene.getAnimation(name); getTextureAnimation; animationEvent({name, frame}, value) delivered to the owner's scripts.

Cost

The library's timelines cost under 4 µs a frame: no allocation or keyed lookup per timeline per frame, and a channel's frames are solved when asked. TimelineCostTests reports it (OWE_TIMELINE_COST_REPORT).

Tests

SceneRenderPrimitivesTests (the clock), ScenePlaybackEaseTests, TimelineRenderTests, TimelineLibrarySweepTests, TimelineLibraryRenderTests, AudioSpectrumTests.testPlaybackRateScalesTheAnalyzersStep.

User guide: Scene wallpapers and Displays and playback rules

Clone this wiki locally