Wallpapers for Windows

PrimoEngine also runs on Windows 11 (23H2 or newer, x64 and ARM64 PCs). The format is the same .livewallpaper v1: a ZIP with manifest.json + content/. The Windows version is out in beta: download it at https://engine.oprimo.dev.

In short: web and video wallpapers work on both systems with the same file. A shader is one package per system: metal on the Mac, hlsl on Windows.

TypeMacWindows
web✅✅ same file, same window.LiveWallpaper API
video✅✅ same file (Premium on both)
metal✅❌ refused: This wallpaper was made for Mac (Metal shader). Look for its Windows version.
hlsl❌ (the Mac doesn’t know the type and rejects the manifest)✅

minMacOS is ignored on Windows.

Shaders on Windows: the hlsl type

An hlsl package is the twin of a metal package: same id, same config, its own entry. With the same id, the app treats both as the same wallpaper and the settings keep their names. To share, send two files, for example aurora-mac.livewallpaper and aurora-windows.livewallpaper.

manifest.json:

{
  "schemaVersion": 1,
  "id": "com.example.hello-shader",
  "version": "1.0.0",
  "title": "Hello Shader",
  "author": { "handle": "@you" },
  "type": "hlsl",
  "entry": "content/shader.hlsl",
  "config": [
    { "key": "speed", "type": "float", "min": 0.1, "max": 3.0, "default": 1.0, "label": "Speed" },
    { "key": "tint",  "type": "color", "default": "#FFFFFF", "label": "Tint" }
  ],
  "capabilities": { "network": [] }
}

content/shader.hlsl (it is hello-shader in HLSL; download it ready as hello-shader-windows):

// Contract with PrimoEngine: keep this struct as-is (32 bytes, register b0).
struct Uniforms {
    float2 resolution;   // drawable size in pixels
    float  time;         // seconds since the wallpaper started
    float  speed;        // config key "speed" (default 1.0)
    float4 tint;         // config key "tint" as RGB 0..1 (a = 1)
};
cbuffer PrimoUniforms : register(b0) { Uniforms u; };

// Required: the pixel function, named exactly f_main.
float4 f_main(float4 fragCoord : SV_Position) : SV_Target {
    float2 uv = fragCoord.xy / u.resolution;          // 0..1, origin top-left
    float t = u.time * u.speed;
    float v = sin(uv.x * 8.0 + t) + sin(uv.y * 6.0 - t * 0.7)
            + sin(length(uv - 0.5) * 14.0 - t * 1.3);
    float3 col = 0.5 + 0.5 * cos(6.28318 * (v * 0.2 + float3(0.0, 0.33, 0.67)));
    return float4(col * u.tint.rgb, 1.0);
}

The contract

Shaders that react to music

With "capabilities": { "audioReactive": true }, the shader gets a summary of the sound the PC is playing, never the audio itself. The values are the Mac’s (see Audio-reactive shaders); only the layout changes, because HLSL pads arrays to 16 bytes:

struct AudioUniforms {
    float4 bands4[8];   // band i = bands4[i >> 2][i & 3], 32 bands from 30 Hz to 16 kHz, 0..1
    float4 levels;      // x bass, y mid, z treble, w overall level
    float  beat, bassTime, levelTime, midTime;
    float4 beatAges;    // seconds since the last 4 beats, newest first; 9 = none
    float  active;      // eases to 0 after ~2 s of silence
};
cbuffer PrimoAudio : register(b1) { AudioUniforms audio; };
Texture2D<float4> history : register(t0);   // optional: 32 × 64, r channel, row 0 = now

float band(int i) { return audio.bands4[i >> 2][i & 3]; }

Read the history with history.Load(int3(band, row, 0)).r. Without music (and in thumbnails) everything is 0 and beatAges is 9: design a good still look.

From Metal to HLSL

MetalHLSL
#include <metal_stdlib>, using namespace metal;, v_maindelete
fragment float4 f_main(float4 p [[position]], constant Uniforms& u [[buffer(0)]])cbuffer PrimoUniforms : register(b0) { Uniforms u; }; and float4 f_main(float4 p : SV_Position) : SV_Target
constant AudioUniforms& audio [[buffer(1)]], audio.bands[i]cbuffer PrimoAudio : register(b1), audio.bands4[i >> 2][i & 3]
texture2d<float> history [[texture(0)]], history.read(uint2(x, y)).rTexture2D<float4> history : register(t0), history.Load(int3(x, y, 0)).r
fract, mix, rsqrt, atan2(y, x)frac, lerp, rsqrt, atan2(y, x)
fmod(x, y)fmod(x, y) (same; GLSL’s mod is x - y * floor(x / y))
float2x2 m; m * vmul(v, m) with the same constructor: the product order flips
float3(1.0)float3(1.0, 1.0, 1.0) or (float3)1.0

Check the result side by side with the Mac: colors and motion should match.

What Windows refuses on import

The app compiles and inspects every hlsl shader on import, wherever it came from:

MessageWhat to do
Shader is missing the required 'f_main' fragment function.Name the pixel function f_main.
Shader didn't compile: …Fix the error shown. #include always fails.
Shader rejected: the source is larger than 512 KB.Make the file smaller.
Shader rejected: … writable …Remove writable resources (RWTexture2D, RWBuffer, structured buffers, atomics).
Shader reads audio (register(b1)) but the manifest doesn't declare …Add "capabilities": { "audioReactive": true }, or drop b1.
Shader rejected: '…' is bound outside …Use only b0, b1, t0 and s0.
Shader rejected: the uniforms … are larger than …b0 holds up to 240 bytes and b1 up to 192.

File names

Windows refuses names the Mac accepts, with Corrupt archive: unsafe path ’…’. Avoid them in any package meant for both:

Pack and test

python3 pack.py <folder> accepts type: "hlsl": it checks the manifest, f_main, the registers and the forbidden resources, and writes the .livewallpaper. It does not compile HLSL; the app does that on import. In PrimoEngine for Windows, import with the Import button or by double-clicking the file.

Windows doesn’t ship Python: install Python 3 from the Microsoft Store (or python.org) and run py pack.py <folder>.

To have an AI write one, use the HLSL shader prompt.