-
-
Notifications
You must be signed in to change notification settings - Fork 60
Create Compatibility
Many web wallpapers were originally written for Wallpaper Engine or Lively Wallpaper, and Sucrose is designed so those wallpapers can run with little or no change. The trick is feature detection: probe for the host's API and fall back to Sucrose's equivalent. This page shows the compatibility patterns the shipped Showcase wallpapers use — for audio, for customization properties, and for detecting which host you are running in — so you can write one wallpaper that works in all three engines.
- The idea: feature-detect, then fall back
- Detecting the host
- Audio compatibility
- Property compatibility
- A portable wallpaper skeleton
- Porting an existing wallpaper to Sucrose
- Tips and gotchas
- See also
All three engines push live data into a web wallpaper, but each exposes a different API surface. Rather than detect by name and branch your whole codebase, detect the specific hook you need and register a small adapter that funnels every host into one internal variable or function. The Sucrose Showcase wallpapers do exactly this: they probe for the Wallpaper Engine / Lively hooks first, then fall back to the Sucrose API.
Sucrose identifies itself through the browser User-Agent. The default UA is:
Sucrose/2.3 (Windows NT 10.0; Wallpaper Engine) SucroseWebKit
So you can detect Sucrose with:
const isSucrose = navigator.userAgent.startsWith("Sucrose");The UA string is overridable by the user via the UserAgent general setting, but it always begins with Sucrose, so the startsWith("Sucrose") test is the reliable check. For more on the bridge and detection, see Create JS Bridge.
Wallpaper Engine and Lively expose window.wallpaperRegisterAudioListener(callback), which calls your callback with an audio array. Sucrose instead calls a function you define — conventionally SucroseAudioData(obj) — where obj.Data is the FFT magnitude spectrum (128 samples; Data[0] ≈ bass; an all-zero array means silence). The portable pattern (taken from the Neo Matrix Showcase wallpaper) is:
let frequencyArray = [];
if (window.wallpaperRegisterAudioListener) {
// Wallpaper Engine / Lively
window.wallpaperRegisterAudioListener(a => frequencyArray = a);
} else if (navigator.userAgent.startsWith("Sucrose")) {
// Sucrose
window.SucroseAudioData = function (obj) { frequencyArray = obj.Data; };
}After this, the rest of your visualizer reads frequencyArray and does not care which host provided it. To actually receive audio from Sucrose you must also declare the hook in SucroseCompatible.json:
{ "SystemAudio": "SucroseAudioData({0});" }Note one difference to handle: Wallpaper Engine's audio array and Sucrose's obj.Data may differ in length and scaling, so normalize before visualizing. See Create Audio API for the full SucroseAudioData payload (the obj also carries now-playing metadata beyond Data).
Wallpaper Engine and Lively deliver customization values via:
window.wallpaperPropertyListener = {
applyUserProperties(props) { /* props is a map of property objects */ }
};Sucrose delivers them through a function you define — conventionally SucrosePropertyListener(name, val) — called once per property (name is the key, val is the full control object, with the value in val.value). To support both, register the Wallpaper Engine object and also define the Sucrose function, routing both into a single apply step:
function applyOne(name, value) {
switch (name) {
case "ui_font_size": setFontSize(value); break;
// ...
}
}
// Wallpaper Engine / Lively: a map at once
window.wallpaperPropertyListener = {
applyUserProperties(props) {
for (const key in props) applyOne(key, props[key].value);
}
};
// Sucrose: one property at a time
window.SucrosePropertyListener = function (name, val) {
applyOne(name, val.value);
};On the Sucrose side, the call shape comes from your SucroseProperties.json PropertyListener template ("SucrosePropertyListener('{0}', {1});"). See Create Property Listener Filters and Create Customization Controls.
Putting both adapters together:
// --- Audio ---
let spectrum = [];
if (window.wallpaperRegisterAudioListener) {
window.wallpaperRegisterAudioListener(a => spectrum = a);
} else if (navigator.userAgent.startsWith("Sucrose")) {
window.SucroseAudioData = obj => { spectrum = obj.Data; };
}
// --- Properties ---
function applyOne(name, value) { /* update your wallpaper state */ }
window.wallpaperPropertyListener = {
applyUserProperties(props) { for (const k in props) applyOne(k, props[k].value); }
};
window.SucrosePropertyListener = (name, val) => applyOne(name, val.value);
// --- Render loop (host-agnostic) ---
function frame() { draw(spectrum); requestAnimationFrame(frame); }
requestAnimationFrame(frame);This page runs unchanged in Wallpaper Engine, Lively, and Sucrose.
-
Add a
SucroseInfo.jsonwithType: 2(Web) andSourceset to your entry HTML file. See Create Package Format. -
Add a
SucroseCompatible.jsondeclaring the hooks you need (SystemAudio, theSystem*keys you read). Without it, Sucrose pushes no live data. See Create JS Bridge. - Keep the Wallpaper Engine hooks and add the Sucrose fallbacks as shown above. Do not remove the originals — feature detection means the same file still works elsewhere.
-
Translate your property schema into
SucroseProperties.json. Where possible, reuse the built-in filter key names (blur,saturate,brightness, …) so behavior matches Sucrose's built-in wrappers. See Create Property Listener Filters. -
Normalize audio data — Sucrose's
obj.Datais 128 FFT samples and may scale differently than Wallpaper Engine's array. -
Serve-over-HTTP — Sucrose serves your page from
http://localhost:{port}/, notfile://; verify relative paths, modules, and WebGL texture loads work. See Create Web Architecture. - Test on a browser engine — only WebView/CefSharp deliver the JS bridge. See Choosing Engines.
-
Detect the hook, not just the name.
if (window.wallpaperRegisterAudioListener)is more robust than parsing the User-Agent for non-Sucrose hosts. -
The Sucrose UA always starts with
Sucrose, even if the user customized it — usestartsWith("Sucrose"). -
Sucrose's property listener fires per property, while Wallpaper Engine's
applyUserPropertiesreceives a map — adapt accordingly (loop the map into your single apply function). -
Declare hooks in
SucroseCompatible.jsonor Sucrose will never call your callbacks, no matter how well you feature-detect. - Audio arrays differ across hosts in length and scale; normalize before drawing.
- Keep everything in one self-contained folder — Sucrose copies the whole directory on import. See Type-Web.
-
Create Audio API —
SucroseAudioDatapayload and FFT details -
Create JS Bridge — detection and
SucroseCompatible.json - Create Property Listener Filters — Sucrose's property delivery and built-in keys
-
Create Customization Controls —
SucroseProperties.jsoncontrol schema - Create Web Architecture — serve-over-HTTP and the data pump
- Type-Web — the Web wallpaper type
- Create Example Wallpapers — Showcase wallpapers using these patterns
Getting Started
- Installation
- System Requirements
- Quick Start
- Portal Interface Tour
- Updating Sucrose
- Uninstalling Sucrose
Wallpaper Types
Using Sucrose
- Managing Library
- Using Store
- Customizing Wallpaper
- Multi-Monitor
- Wallpaper Cycling
- Choosing Engines
- Performance Rules
- Theme, Tray & Startup
- Discord Rich Presence
Settings Reference
- Settings Overview
- Settings: General
- Settings: Personal
- Settings: Performance
- Settings: Wallpaper
- Settings: System
- Settings: Other
- Settings: All Keys
Creating Wallpapers
- Create Overview
- Create: Step By Step
- Create: Package Format
- Create: Customization Controls
- Create: JS Bridge
- Create: Audio API
- Create: System API
- Create: Property Listener & Filters
- Create: Web Architecture
- Create: Compatibility
- Create: Example Wallpapers
- Create: Sharing & Publishing
Engine Reference
- Engines Overview
- Engine: MpvPlayer
- Engine: VlcPlayer
- Engine: WebView
- Engine: CefSharp
- Engine: Nebula
- Engine: Vexana
- Engine: Xavier
- Engine: Aurora
- Engine Comparison
Automation & Command Line
Architecture & Internals
- Architecture Overview
- Lifecycle
- Commandog Dispatcher
- Single-Instance Mutexes
- IPC
- Backgroundog Service
- Crash Reporting
- Update Internals
- Property Service
- Undo Internals
Data, Files & Diagnostics
Building & Contributing
- Building From Source
- Repository Layout
- Shared Item Projects
- Code Conventions
- Preprocessor Symbols
- Publish Pipeline
- Bundle Installer Internals
- Extending Sucrose
- Contributing
- Translating with Localizer
- Localization Coverage
- Security Policy
- Privacy & Telemetry
Help & Support