Skip to content

SceneScript Runtime

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

SceneScript runtime

SceneScript is Wallpaper Engine's JavaScript API for scenes. Open Wallpaper Engine runs it on JavaScriptCore, one runtime per wallpaper instance, on its own thread. The evidence and plan, including the specification assembled from WE's own type declarations and documentation, are in docs/scenescript-plan.md.

Shape

Piece Role
SceneScriptRuntime (Runtime/) One JSVirtualMachine and JSContext per instance, WE's prelude, every script of the scene as a record in runtime.js, and the load and frame drivers
SceneScriptThread A serial queue off the main thread; a script that hangs until the watchdog fires stalls only its wallpaper's scripts, never the UI
SceneScriptWallpaper (Host/) Ties a runtime and its extensions to the renderer that owns it
Object table (Objects/, Host/SceneScriptTableSync) Layer, effect and material objects; the renderer writes each object's drawn values before the frame and draws what scripts wrote after it
Modules/ A tokenizer and module transformer that turns WE's ES modules into records the runtime loads
Extensions Engine/ (engine, input, console, localStorage), Audio/ (registerAudioBuffers), Cursor/ (events and hit testing), Media/ (Now Playing), Binding/ (script properties and user properties)

JavaScript-side code lives in bundled .js resources, not Swift strings.

A frame

  1. The renderer calls submit(_:) at the start of each draw with the frame's inputs (time, cursor, audio, properties).
  2. The scripts run on their thread (asyncFrame) and leave a SceneScriptFrameState.
  3. The renderer waits briefly for it, so what scripts did is drawn in the same frame, as in WE. A slow script drops script frames, not render frames.

What is implemented

  • WE's lifecycle: init(), update(value) every frame, destroy, applyUserProperties (changed keys only), resizeScreen, the media* callbacks, animationEvent.
  • WE's object model: thisScene, thisLayer, engine, input, layers, effects (getEffect(...).visible), materials (getMaterial, setMaterialProperty), animations (getAnimation, getTextureAnimation).
  • createLayer from an asset (image, text, shape, particle system, sound), destroyLayer, sortLayer, getLayerIndex.
  • Sound layers under script control; localStorage; timers.
  • Cursor events (cursorMove, cursorDown, cursorUp, cursorClick, cursorEnter, cursorLeave) on solid layers with hit testing, in scene space; clicks count only on the wallpaper.
  • Live left/right registerAudioBuffers (16/32/64 bands).
  • Now Playing, including on macOS 15.4+ through a small adapter (see Audio system).
  • WE's JS modules and classes (WEMath, WEVector, WEColor, Vec/Mat) from the assets.
  • Puppet and model posing through the animation-layer and bone APIs.
  • Return values converted as WE converts them (vectors, numbers, strings, flags).

Performance

  • The com.apple.security.cs.allow-jit entitlement lets JavaScriptCore JIT-compile scripts.
  • No per-frame closures or intermediate arrays per bound script; material writes that change nothing send no command; strings are flushed only when they changed.
  • Script exceptions are deduplicated and logged with repeat counts.
  • SceneScriptLibraryCostTests reports each library script's cost (OWE_SCRIPT_COST_REPORT); SceneScriptCorpusReplayTests replays every library script (OWE_REPLAY_REPORT).

Open points

  • Animation layers and bones are complete with models and puppets; WP12's remaining items are tracked in the plan.
  • A particle rate bound to a script can cost 0.5–4 ms a frame in the script engine.

User guide: Scene wallpapers

Clone this wiki locally