Skip to content

Create Compatibility

Taiizor edited this page Jun 5, 2026 · 1 revision

Create: Wallpaper Engine & Lively 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.

Contents

The idea: feature-detect, then fall back

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.

Detecting the host

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.

Audio compatibility

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).

Property compatibility

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.

A portable wallpaper skeleton

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.

Porting an existing wallpaper to Sucrose

  1. Add a SucroseInfo.json with Type: 2 (Web) and Source set to your entry HTML file. See Create Package Format.
  2. Add a SucroseCompatible.json declaring the hooks you need (SystemAudio, the System* keys you read). Without it, Sucrose pushes no live data. See Create JS Bridge.
  3. 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.
  4. 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.
  5. Normalize audio data — Sucrose's obj.Data is 128 FFT samples and may scale differently than Wallpaper Engine's array.
  6. Serve-over-HTTP — Sucrose serves your page from http://localhost:{port}/, not file://; verify relative paths, modules, and WebGL texture loads work. See Create Web Architecture.
  7. Test on a browser engine — only WebView/CefSharp deliver the JS bridge. See Choosing Engines.

Tips and gotchas

  • 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 — use startsWith("Sucrose").
  • Sucrose's property listener fires per property, while Wallpaper Engine's applyUserProperties receives a map — adapt accordingly (loop the map into your single apply function).
  • Declare hooks in SucroseCompatible.json or 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.

See also

Home

Getting Started

Wallpaper Types

Using Sucrose

Settings Reference

Creating Wallpapers

Engine Reference

Automation & Command Line

Architecture & Internals

Data, Files & Diagnostics

Building & Contributing

Help & Support

Clone this wiki locally