Skip to content

Releases: Delido/signalrgb-wallpaper

v2.3.13-beta — water sRGB color-space fix + Configurator i18n sweep

17 Jun 13:11

Choose a tag to compare

Fixed — Water mode shifted the whole BG, not just the ring band

The v2.3.12 scale=0 fix stopped the activation jump, but during an active ripple the BG still appeared shifted across the entire viewport — not just where the ring band crossed.

Root cause: SVG filter color-space mismatch. Chromium runs feDisplacementMap in linear-RGB by default, but our displacement map canvas exports as sRGB-encoded PNG. The sRGB → linear conversion remaps R=128 from 0.502 down to ~0.215, which means the "neutral grey" the canvas paint treats as 0-displacement actually encodes (0.215 - 0.5) × 80 = -22.8 px displacement across the entire non-ring area of #bg. Net visual: the whole BG slid -22 px up-left whenever a ripple was in flight.

Fix: color-interpolation-filters="sRGB" on the filter element. Filter chain operates in sRGB throughout, matching the PNG encoding. R=128 produces true 0 displacement at neutral; only the ring band's painted gradients actually bend pixels. No whole-BG shift, no edge gap, no ghost overlay.

Configurator i18n sweep

User reported large chunks of the configurator still rendering in English when DE was selected:

  • applyI18n attribute-targeting rewrite — the pre-v2.3.13 data-i18n-attr mechanism collected the attribute name as a {attr} template parameter instead of using it as the target attribute. The Library search placeholder stayed English regardless. Added independent data-i18n-title / data-i18n-placeholder / data-i18n-aria-label / data-i18n-alt slots that each carry their own translation key, on top of the main data-i18n for textContent.
  • Quick Looks bundles (10 × name + desc = 20 entries) now i18n-driven. Each LOOK_BUNDLES entry carries nameKey/descKey, routed through t() in renderLookBundles + applyLookBundle's confirm dialog.
  • Widget catalog labels (13 widget type names) now i18n-driven via a new _widgetLabel(def) helper. Five direct def.label reads converted (layout preview, Add-Widget grid, widget list, modal title, remove toast) + two toast templates ("Added X" / "Removed X").
  • Static dropdown options got data-i18n attributes + matching translation rows: glow layout (5), grid renderer (2), glass quality (3), frame rate (3).
  • Header tooltips for Live preview / Show tour / Open library via the new data-i18n-title slot.
  • renderAll now re-builds the Add-Widget grid + Quick-Looks grid on language switch — both were built once at boot.

Upgrade

Wallpaper bundle re-import required. Tray → Re-import wallpaper bundles.

Beta — no winget submission, GitHub Releases only.

v2.3.12-beta — real fix for water-mode BG shift

17 Jun 12:45

Choose a tag to compare

The v2.3.11-beta pre-seed via toDataURL didn't work. Data: URLs load asynchronously, so the SVG filter's first paint still saw an empty in2 input and shifted #bg by -40 px until the load completed a frame or two later.

Fixed — water-shift, for real this time

Flipped the strategy. feDisplacementMap scale now starts at 0, which makes the displacement provably 0 px no matter what in2 resolves to:

  • splash() bumps scale to ACTIVE_SCALE (80) before the next tick paints
  • The tail-tick that runs after the last ripple expires drops scale back to 0
  • disable() also resets scale to 0 as belt-and-suspenders

No more activation jump. The BG only moves when a wave is actually in flight.

Upgrade

Wallpaper bundle re-import required. Tray → Re-import wallpaper bundles.

Beta — no winget submission, GitHub Releases only.

v2.3.11-beta — water-shift fix + drop All + on-screen lock pill

17 Jun 12:39

Choose a tag to compare

Three follow-up fixes off v2.3.10-beta's water rebuild.

Fixed — Water mode shifted the wallpaper image on activation

feDisplacementMap treats a missing in2 input as R=G=B=0, which yields a uniform (0/255 - 0.5) × scale shift — at scale=80 that's -40 px across the whole BG. The wallpaper visibly jumped up-left as soon as you picked water mode, then snapped back only after the first click painted a real displacement map.

Fix: pre-seed feImage with the neutral-grey canvas (rgb(128,128,0)) inside ensureFilter() so the displacement starts at zero from frame 1.

Changed — "All" mode removed from Pixelfx

Stacking trail + glow + click ripple at once was visually muddy and weakened the single-select intent of the mode picker. Removed:

  • The "All" tile from the Configurator (PIXELFX_MODES)
  • The "all" value from the wallpaper-side allow-list in setMode
  • The mode === "all" branches in wantsTrail/wantsGlow/wantsRipple (now strict equality)
  • The "All" entry in the tray menu's PIXELFX_MODES_TRAY

Bonus: the tray menu gained the "Water ripple" entry it was missing. Any persisted pixelfx="all" falls through to "off" — pick a new single mode in the Configurator.

Added — On-screen lock pill

While widgets are unlocked (edit mode), a small floating pill appears at top-center of the wallpaper:

[Widgets unlocked]  [🔒 Lock]

Click the lock button and widgets re-lock without going back to the Configurator. The pill disappears immediately. Reuses the pre-existing #widgets-banner CSS scaffolding (which was previously dead code).

Upgrade

Wallpaper bundle re-import required. Tray → Re-import wallpaper bundles.

Beta — no winget submission, GitHub Releases only.

v2.3.10-beta — Water ripple is now actual BG refraction

17 Jun 12:31

Choose a tag to compare

The v2.3.3-beta "Water ripple" mode was three concentric outline strokes per click. Visually it read as an inflated click ring, not as water. Rebuilt from the ground up to use real SVG-displacement refraction on the wallpaper image.

Changed — Water ripple uses SVG feDisplacementMap on #bg

New water IIFE module owns its own SVG <filter id="pixelfx-water-filter"> with feImage + feDisplacementMap (scale=80), applied via body.fx-pixelfx-water #bg. Only the BG image gets the filter — the glow bars stay stable so they don't read as part of the wave.

Per-click behavior:

  • Two staggered ripples spawn per click (the second 0.12 s behind the first → single click produces a visible follow-up wave)
  • rAF chain paints expanding ring fronts into a 256×256 displacement-map canvas
  • 48 angular slices per ripple: outer band encodes radial outward push (R/G shifted by +cos θ × amp / +sin θ × amp), inner band encodes radial inward pull (opposite sign)
  • Activity-gated toDataURL + setAttribute("href") — encoding stops when the last ripple expires (gotcha-uncapped-raf-dpr pattern, same memory discipline Liquid Distortion already uses)

Architecture mirrors the existing Liquid Distortion (mousefx.ripple) which uses the same SVG-displacement technique for cursor-driven ripples — but with a separate filter ID so both effects can run independently.

The pixelfx canvas is stopped (backing buffer → 1×1) in water mode since it no longer participates in the render.

Visual result

The wallpaper image actually bends along a propagating wave front. Reads as water on the surface of your screen, not as a colored halo on top.

Upgrade

Wallpaper bundle re-import required. Tray → Re-import wallpaper bundles.

Beta — no winget submission, GitHub Releases only.

v2.3.9-beta — Pixelfx (Cursor) section → fx-tile grid

17 Jun 12:22

Choose a tag to compare

Configurator polish — the last .seg button row in the Effects tab moves to the same tile layout the rest of the tab already uses.

Changed — Pixelfx (Cursor) → fx-tile grid

The Pixelfx mode row (Off / Trail / Hover glow / Click ripple / Water ripple / All) was the last segmented-button strip in the Effects tab. Every neighbouring control (ambient presets, cursor distortion effects) had already moved to a .fx-tile grid; this brings Pixelfx in line.

Each mode now gets:

  • An emoji icon (🚫 / ✨ / 💡 / 💫 / 💧 / 🎆)
  • A short label
  • A one-line description (DE + EN)

Active state is the same .active class the other tile grids use. Selector behaviour is unchanged — Pixelfx is single-select (radio), unlike the stackable mousefx grid. Existing pixelfx setting values migrate 1:1; nothing in the wallpaper bundle changes.

Fixed — mousefx tile collector was unscoped

Bonus correctness fix that fell out of the above. The mousefx tile click handler used an unscoped document.querySelectorAll(".fx-tile.active") to gather mouseEffects state. Once the pixelfx grid also used .fx-tile, an active pixelfx tile would have been swept into the array as an undefined entry. Scoped both the bind loop and the renderAll loop to #mousefx-tiles.

Upgrade

Configurator-only change — no wallpaper bundle re-import needed. WALLPAPER_VERSION stays at 2.3.8-beta.

Beta — no winget submission, GitHub Releases only.

v2.3.8-beta — Three more GPU-budget wins

17 Jun 09:14

Choose a tag to compare

Three more GPU-budget wins on top of v2.3.7-beta's Repulsion fix.

Fixed — Stripes/Pills layouts held N in-flight CSS transitions

Same regression class as the Repulsion fix (and the v1.0-era grid-zone transition before it). The Vertical Stripes / Horizontal Stripes / Centered Pills bar layouts each carried a transition: background 0.08s linear on their .zone elements. JS rewrites the --c CSS var per zone every render tick (~30 Hz), restarting the 80 ms transition every 33 ms — N background transitions juggling continuously. Typical 16-30 zone stripes layout = 16-30 in-flight CSS animations for no visible benefit, since JS updates were already smooth. Dropped the transition; visuals unchanged.

Fixed — Audio-spectrum widget ran at 1 fps

Generic 1 Hz widget setInterval was redrawing the FFT bars — once per second — even though lastAudio was refreshed at the host's ~30 Hz FFT cadence. Added a reg.fastTick opt-in plus a counter-gated rAF loop (self-stops at 0 fast widgets, pause-guarded, resume-rearmed) that walks fast widgets at RENDER_INTERVAL_MS. The 1 Hz path skips fast widgets so there's no double redraw.

Changed — Pixelfx + audioglow now use SCALE × DPR (like ambient)

The ambient particle canvas already routed through _qualityScale() (0.5/0.75/1.0 per bucket) on top of _qualityDpr(). Pixelfx (cursor trail / glow / ripple / water) and audioglow (radial pulse / wide spectrum / wave) only had the DPR multiplier — full viewport in Performance bucket.

Both canvases now apply the SCALE multiplier too: a 5120×1440 surface drops from 7.4 Mpx to 1.85 Mpx in Performance bucket (4×) and 4.2 Mpx in Balanced (~1.75×). Content is soft (alpha gradients, blurred sprites) so bilinear upscale is visually indistinguishable. Quality bucket stays at native 1:1 × DPR 2.

Upgrade

Wallpaper bundle re-import required. Tray → Re-import wallpaper bundles.

Beta — no winget submission, GitHub Releases only.

v2.3.7-beta — Drop Repulsion CSS transition (GPU perf)

17 Jun 08:57

Choose a tag to compare

GPU-side performance fix on top of v2.3.6-beta's memory pass.

Fixed — Widget Float repulsion kept N×2 CSS animations in flight

The Widget Float effect wrote --repel-x / --repel-y CSS custom properties on every widget every 33 ms (30 Hz tick). A separate body.fx-repulsion .widget rule wrapped both vars in a 0.18 s cubic-bezier transition — so every 33 ms rewrite restarted the 180 ms transition.

With N widgets the compositor kept 2N CSS animations in flight at all times, juggling them every frame. Position output was already smoothed twice (once by the JS dt-based interpolation, once by the CSS easing), so the GPU was paying for an N-widget animation set that didn't need to exist. Removed the transition; the 30 Hz JS updates are perceptually smooth on their own.

Same class of regression gotcha-perf-transitions warned about (the v1.0-era grid-zone background transition was the original culprit).

Upgrade

Wallpaper bundle re-import required. Tray → Re-import wallpaper bundles.

Beta — no winget submission, GitHub Releases only.

v2.3.6-beta — Collapse idle canvases (≈ -150 MB on 2-monitor setups)

17 Jun 08:46

Choose a tag to compare

Memory-footprint optimisation pass.

Changed — idle canvases collapse to 1×1

The wallpaper page hosts four full-viewport canvases:

  • ${''}#bars-canvas${''} — glow grid in canvas-renderer mode
  • ${''}#ambient-canvas${''} — snow / rain / sparks / aurora / storm / …
  • ${''}#pixelfx-canvas${''} — trail / hover-glow / click ripple / water
  • ${''}#audioglow-canvas${''} — pulse / spectrum bars / waveform

Pre-v2.3.6-beta every one of them kept a viewport-sized backing buffer even when the effect was off. At 5120×1440 × DPR=1 × 4 bytes per pixel that's ~30 MB per canvas → ≈ 100 MB resident per wallpaper instance with three of the four idle. Multi-monitor setups doubled or quadrupled that because each monitor runs its own WebView2 instance.

Each effect's ${''}stop()${''} (plus the canvas-off branch in ${''}_syncGridCanvasVisibility()${''} for the bars canvas) now sets ${''}canvas.width = canvas.height = 1${''} after clearing. ${''}start()${''} already calls ${''}resize()${''} to set the canvas back to full viewport on re-enable, so the trade-off is one allocation per off→on transition.

Realistic ceiling on a 2-monitor 5120×1440 setup with three of the four effects idle: ≈ -150 MB RAM.

Upgrade

Wallpaper bundle re-import required. Tray → Re-import wallpaper bundles.

Beta — no winget submission, GitHub Releases only.

v2.3.5-beta — Cursor effects UX + fullscreen pause fix

17 Jun 08:26

Choose a tag to compare

Three quick UX fixes on top of v2.3.4-beta after the first round of real-world feedback.

Fixed — cursor effects ignored fullscreen pause

The four MouseFx ticks (Liquid Distortion, Chromatic Halo, Spotlight, Widget Float) were the only rAF loops in the wallpaper page that didn't short-circuit on isPaused. Every other tick (ambient, audio-glow, pixelfx, parallax, renderFrame) already bailed and the wallpaper-resume event re-armed them on unpause. Now all four follow the same pattern, and a fresh resume listener kicks them back to life when fullscreen pause clears — no more silent CPU drain while you game or watch a film.

Changed — moved from Widgets tab to Effects tab

The four cursor distortion effects lived in the Widgets card because that's where they shipped from, but they alter the wallpaper visuals, not widget layout. Moved into the Effects card right under the existing Pixelfx (cursor) row where they conceptually belong.

Changed — 4 checkboxes → tile grid

The old labeled-checkbox row was bland and didn't surface what each effect actually does. New tile grid: emoji + name + one-line description on each tile. Active tiles get a blue tint + border so the state reads at a glance. Same four effects, same mouseEffects array persistence — UI only.

Upgrade

Wallpaper bundle re-import required for the fullscreen-pause fix to land (Lively / Wallpaper Engine cache the bundle in a random-hash folder). Tray → Re-import wallpaper bundles or re-add the freshly installed zips manually.

Beta — no winget submission, GitHub Releases only.

v2.3.0 — In-app pack browser, atmosphere effects, widget skins, cursor overhaul, GPU/RAM pass

17 Jun 13:15

Choose a tag to compare

Six months of beta iteration consolidated into one stable release. v2.2.1 → v2.3.0 covers v2.3.0-beta through v2.3.13-beta.

Highlights

In-app wallpaper pack browser

The Library tab browses curated pack manifests hosted on GitHub Pages. Tiles preview every bundled wallpaper; click adds the whole pack to the local library. New packs ship without a Configurator update.

Atmosphere effects

  • Storm preset — rain particles + a lightning flash subsystem with random strikes and occasional doubles
  • Water-ripple pixelfx — clicks bend the wallpaper image along a propagating SVG-displacement wave (real BG refraction, not an outline overlay)
  • Weather-reactive ambient — picks the right preset from the live WMO code (Open-Meteo)

Widget skin system

Every widget type can ship alternate skins. Weather launches with three: default, compact, hexagon. Skin picker lives in the per-widget edit dialog.

Cursor-effect overhaul

  • Liquid Distortion / Chromatic Halo / Spotlight / Widget Float moved to the Effects tab as a tile grid (replacing the v1.6-era checkbox row)
  • Pixelfx mode picker also became a tile grid in v2.3.9
  • New on-screen lock pill: re-lock widgets without going back to the Configurator

GPU + memory optimisation pass

  • Idle full-viewport canvases (ambient / pixelfx / audio-glow / bars) collapse to 1×1 backing buffer (~30 MB saved per canvas per WebView2 instance on a 5120×1440 setup)
  • Pixelfx + audio-glow gained the same SCALE × DPR bucket the ambient canvas already had — Performance bucket = 4× fewer pixels per frame
  • Widget Float repulsion no longer juggles 2N in-flight CSS transitions every render tick
  • Stripes/Pills layouts no longer hold N background transitions for the same reason
  • Audio-spectrum widget redraws at 30 Hz via a new counter-gated rAF path (was stuck at 1 fps)

Configurator i18n sweep

  • applyI18n gained independent attribute translation slots (data-i18n-title, data-i18n-placeholder, data-i18n-aria-label, data-i18n-alt)
  • All Quick Looks bundles, widget catalog labels, glow / glass-quality / frame-rate / grid-renderer dropdowns, and header tooltips now follow the active language
  • Add-Widget grid + Quick-Looks grid rebuild on language switch

Notable fixes from the beta line

  • Water-mode whole-BG shift (v2.3.13) — SVG filter color-space mismatch. Chromium runs feDisplacementMap in linear-RGB by default but our displacement map canvas exports as sRGB PNG. R=128 sRGB → 0.215 linear → constant -22 px shift across the non-ring area. Added color-interpolation-filters="sRGB" on the filter.
  • Memory leaks (v2.3.4 / v2.3.5) — Liquid Distortion's per-frame toDataURL was leaking ~1 MB/s of decoded SVG-filter bitmaps; Spotlight allocated a fresh CSS gradient string per frame
  • Cursor effects ignored fullscreen pause (v2.3.5)
  • Library bulk-select / tag-filter edge cases (v2.3.1 / v2.3.2)

Upgrade

Wallpaper bundle re-import required. Tray → Re-import wallpaper bundles.

Full per-beta history in CHANGELOG.md.