A wallpaper picker for linux-wallpaperengine on Hyprland. It shows your Steam Workshop wallpapers in a rofi grid with thumbnails. It sets them per monitor or spans one across all of them, crossfades between them, finds new ones on the Workshop, remembers everything across logins, and recolours your desktop to match.
- Thumbnail grid of every Wallpaper Engine wallpaper you're subscribed to, searchable by title, tag, type or workshop ID. Items linux-wallpaperengine can't play (presets, unfinished downloads) are hidden.
- Per-monitor or spanned. Press Enter for all monitors, Alt+1…9 for one monitor (numbered left to right), or Alt+S to span one wallpaper across every screen.
- Crossfades. The new wallpaper starts next to the old one and replaces it only once it's drawing, so you never see a blank or half-loaded frame. If it fails to start, the old one stays.
- Colours follow the wallpaper. With Caelestia, the shell switches to a dynamic Material You scheme taken from the wallpaper as it renders, including its own settings, not from its Workshop preview. pywal, matugen or anything else can be hooked in with one config line. Alt+C turns it on or off, Alt+R re-captures.
- Workshop search. Type a query and press Alt+F to search the Steam Workshop with thumbnails, no API key needed. Picking a result opens it in Steam so you can Subscribe, and we-switch applies it as soon as Steam has downloaded it.
- Web wallpapers keep their settings. Choices made in a wallpaper's own menu survive restarts and reboots.
- Per-wallpaper overrides of any property in the config, such as a wallpaper whose default is broken.
- Themed to match. The picker reads the current Caelestia scheme (or pywal colours) each time it opens.
- Survives crashes and logins. The engine runs as a systemd user unit that retries if it crashes on start, and
we-switch restorebrings your last wallpaper back at login.
| Searching | Results |
|---|---|
![]() |
![]() |
- Hyprland (monitors and the cursor come from
hyprctl) - linux-wallpaperengine, ideally with the patches below, plus Wallpaper Engine owned on Steam
- rofi 1.7.6+ or 2.x (Wayland)
- Python 3.11+ and Pillow (
python3-pillow) - systemd (user session)
- Optional: the
caelestiaCLI for automatic colours,ffmpegfor sharper stills of video wallpapers
git clone https://github.com/adamescj/we-switch ~/src/we-switch
ln -s ~/src/we-switch/we-switch ~/.local/bin/we-switchAdd these lines to your Hyprland config. Use full paths, because exec-once may not have ~/.local/bin on PATH:
exec-once = ~/.local/bin/we-switch restore
bind = SUPER, W, exec, ~/.local/bin/we-switch
# crossfade between wallpapers, and let the picker fade instead of slide
layerrule = match:namespace linux-wallpaperengine, animation fade
layerrule = match:namespace rofi, animation fadewe-switch alternates the engine between the bottom and background layers so it can crossfade. If your shell draws its own wallpaper, turn that off, or it can end up on top of the engine. For Caelestia, in ~/.config/caelestia/shell.json:
"background": { "enabled": false }If something else already starts linux-wallpaperengine at login, remove it. we-switch stops engines it didn't start, so the two would keep replacing each other.
Caelestia users: tapping Super opens the launcher on release. Caelestia's stock config cancels that with a
catchallbind. If you've replaced that bind with per-key interrupts, add one for W too:bindin = Super, W, global, caelestia:launcherInterrupt
patches/linux-wallpaperengine.patch fixes bugs in linux-wallpaperengine and adds two small hooks we-switch uses. It applies to upstream commit b016d7d:
cd linux-wallpaperengine
git apply ~/src/we-switch/patches/linux-wallpaperengine.patch
cmake --build build| Change | Without it |
|---|---|
| Web frames are copied into the wallpaper's texture, not its framebuffer's ID | Web wallpapers can come up black (whenever the two GL names differ) |
Properties are passed to web pages (wallpaperPropertyListener.applyUserProperties) |
Web wallpapers ignore their settings and --set-property; some never start drawing |
| Text properties lose their JSON quotes | Pages get "\"2000-01-01\"" and can't parse it |
Text objects accept padding as "x y" |
Some scenes abort with type must be number, but is string |
LWE_CEF_CACHE sets a lasting browser profile |
Web wallpapers forget their own settings on every restart |
SIGUSR1 retakes the --screenshot |
"Colours follow wallpaper" falls back to the Workshop preview |
we-switch works without the patch. Those features degrade as listed.
| Key in the picker | Action |
|---|---|
| Enter / click | Set on all monitors |
| Alt+1 … Alt+9 | Set on that monitor only; the others keep theirs |
| Alt+S | Span across all monitors |
| Alt+C | Colours follow wallpaper: on / off |
| Alt+R | Recolour from the wallpaper as it looks right now |
| Alt+F | Search the Steam Workshop for what you've typed |
| Esc / Super+W | Close |
From a terminal:
we-switch # open the picker
we-switch list # ID, type, title of every installed wallpaper
we-switch set 2539277254 --span # span one wallpaper
we-switch set 1324146312 --monitor DP-2
we-switch search cyberpunk city # Workshop search; empty = trending this week
we-switch recolour # re-take colours from the wallpaper as it looks now
we-switch stop
journalctl --user -u 'we-switch-engine-*' # engine logsThe search reads Steam's public Workshop pages, so it needs no API key or login. It skips Applications and presets, which linux-wallpaperengine can't play, and Mature or Questionable items unless you set allow_mature = true.
| Key in the results | Action |
|---|---|
| Enter | Installed: apply it. Otherwise: open it in the Steam client to subscribe, wait for the download (up to 10 minutes), then apply it |
| Alt+O | Open the Workshop page in your browser |
| Alt+F | New search |
Installing goes through the Steam client: downloading Workshop items needs your Steam account, and we-switch never handles your login.
Optional. Copy config.example.toml to ~/.config/we-switch/config.toml and change what you need: workshop folder, FPS, scaling, fullscreen pausing, engine flags, property overrides, grid size, font, or the colour command.
A few defaults worth knowing:
--no-audio-processing: without it, linux-wallpaperengine opens a PipeWire capture stream for audio-reactive wallpapers, which can break other audio on some setups. Setaudio_processing = truefor visualisers.fullscreen_pause = "active": pause only while a fullscreen app is on a workspace you can see. The engine's default pauses for any fullscreen window, including one in a hidden special workspace, which can leave the wallpaper frozen for good, and a web wallpaper started while paused stays black.
Files: state in ~/.local/state/we-switch/, web wallpaper profiles in ~/.local/share/we-switch/profiles/, thumbnails and captures in ~/.cache/we-switch/.
MIT



