Repository navigation
Architecture
A summary of docs/architecture.md: how Open Wallpaper Engine is divided into modules, which way dependencies point, how a scene flows from files to pixels, and how one wallpaper runs on several displays.
┌──────────────────────── App shell (SwiftUI/AppKit) ────────────────────────┐
│ App/ Library/ Workshop/ Settings/ UI/ (views + view models) │
└───────────────┬───────────────────────────────┬────────────────────────────┘
│ WEWallpaper │
┌─────────▼─────────┐ ┌─────────────┐ ┌──▼──────────┐
│ Scene/ (engine) │ │ Video/ │ │ Web/ │
│ Format → Values │ │ AVPlayer + │ │ WKWebView + │
│ Shaders → Render │ │ music sync │ │ WE web API │
│ Scripting, Audio │ └──────┬──────┘ └─────────────┘
└─────────┬─────────┘ │
└──────────┬────────┘
┌──────▼──────┐
│ Audio/ │ system capture (process tap or SCK), item taps
└─────────────┘
Core/ logging, diagnostics, settings store, asset locations: usable by everything above
Dependency direction: arrows point down only. Scene/ never references UI/, Library/, Settings/ views or AppDelegate. Core/ depends on nothing else in the app. The engine (Scene/, Audio/, Core/) is meant to become a local Swift package later, so it can be unit-tested and run headless; the rules make that a move, not a rewrite.
| Folder | Responsibility |
|---|---|
Core/ |
Logging (OWELog, signposts, frame metrics), the settings model and store, the WE assets location, the Objective-C exception catcher, the instance registry, the Keychain |
Scene/Format/ |
Decode WE files into plain Swift models, with no side effects: scene.json, project.json, effect.json, materials, models, particles, .pkg, .tex
|
Scene/Values/ |
Resolve every dynamic value the same way: literal, {"user":…}, {"user":{"name","condition"}}, {"script":…}, {"animation":…}
|
Scene/Shaders/ |
GLSL → SPIR-V → MSL translation, reflection, the variant cache, the pipeline archive, engine combos |
Scene/Rendering/ |
Metal: layers, the effect pass graph, render targets, text, particles, models, lights, the camera |
Scene/Scripting/ |
The SceneScript runtime (JavaScriptCore), one per wallpaper instance on its own thread; Host/ ties it to the renderer |
Scene/Loading/ |
Turns a wallpaper into render content: loads, resolves and builds |
Scene/Sound/ |
Sound layers, mixing and spatialisation |
Scene/UI/ |
Scene-specific SwiftUI (the inspector, user properties); the only scene files allowed to import SwiftUI views |
Audio/ |
System audio capture with a restart lifecycle, per-player taps, the spectrum; one producer, many consumers |
Video/, Web/
|
Each type's player, view, view model and features |
Library/ |
The library model, import paths, playlists, installed listing, property scopes |
Workshop/ |
SteamCMD, the Workshop API, downloads, dependencies, the assets installer |
Settings/, UI/, App/
|
Settings pages; the main window and shared components; entry point, AppDelegate, windows, menus, playback monitoring, safe restart |
Each view model lives next to its view. No Wallpaper Engine files ship in the repository or the app; Core/WallpaperEngineAssets resolves them at run time (see Assets system).
-
Load.
Scene/Formatdecodesproject.json,scene.json(from disk or the.pkg), then models, materials, effects and textures. -
Resolve.
Scene/Valuesbinds user properties (per wallpaper), scripts and animations to typed values. Nothing downstream reads raw JSON. -
Build.
Scene/Loadingproduces the render content: an ordered layer list in authored order, each layer with its parent transform, effect pass graph, and text, particle and sound state. -
Render.
Scene/Renderingexecutes the pass graph each frame through translated WE shaders, with uniforms from reflection, built-ins (g_Time, resolutions, pointer, audio spectrum) and resolved constants. -
Script.
Scene/Scriptingruns once per frame on its own thread. Scripts read and write a shared object table; the renderer feeds it the drawn values before the frame and draws what scripts wrote after it.
Details: Scene pipeline.
A wallpaper runs once, however many displays show it with the same user properties.
-
Registry.
WallpaperInstanceRegistryholds running instances keyed byWallpaperInstanceKey(folder, file, type and the property store). Each display holds aWallpaperInstanceLease; an instance stops one main-queue turn after the last release, so a rebuilt display takes hold again first. -
Properties per display. Each display has its own store (
SceneUserProperties.<identity>.display.<id>), started from the shared one. "Sync properties across displays" makes every display use the shared store.WallpaperPropertyGroupsregroups displays whose stores are equal into one instance. -
Scenes.
SceneWallpaperInstanceowns the loader, oneSceneMetalRendererand the observers. Each display is aSceneWallpaperPresenterwith its ownMTKView. With several displays, the one with the highest frame rate drives: the frame renders once at the largest target any display needs, then each display presents it at its own size and placement. - AVKit videos. One player per video; each display's view shows it.
-
Web. A
WKWebViewcan't be in two windows, so each display keeps its page; only the audible display's page plays sound. -
Sound (
WallpaperAudioRouting) plays once per running wallpaper. - The watchdog gets one frame time per rendered frame of an instance.
- WE semantics, not approximations. WE's shaders and effect definitions are the reference.
- Per-wallpaper state. State belongs to an instance, never to a process-wide singleton.
- Loud failure. Failures are logged once, with the wallpaper, layer and reason.
- Honest caches. Everything derived is keyed on its inputs and the version of the code that produced it.
See also Contributing and WE-fidelity principles.
User guide: What is Open Wallpaper Engine? and Displays and playback rules
Open Wallpaper Engine · GPL-3.0 · Released by Deepratna Awale · Based on Open Wallpaper Engine by Haren Chen and MrWindDog · Not affiliated with Wallpaper Engine or Valve · Home · User Guide · Developer Guide