While I no longer spend hours searching for a new wallpaper that seems to speak to my personality, I do like a decent looking wallpaper. Out of the box Hyprland provides some wallpaper switching, but I wanted to enhance it a bit.

The goal was to have a folder on my computer where I could just drop a new wallpaper and then have it change on each login. My kids also happened to be looking over my shoulder when I was choosing wallpapers so that let them get in on the action, each choosing a wallpaper for my desktop.

The pieces

  • swww — a Wayland wallpaper daemon with smooth animated transitions. Yes I'm aware it's been moved and renamed.
  • hypridle — Hyprland's idle management daemon; triggers commands on lock/unlock/sleep
  • hyprlock — the lock screen
  • A small shell script that picks a random image and hands it to swww

The script

Drop this somewhere on your $PATH. I keep mine at ~/.local/bin/rotate-wallpaper.sh.

#!/usr/bin/env bash
set -euo pipefail

WALLPAPER_DIR="${1:-$HOME/Pictures/wallpaper}"

mapfile -t images < <(find "$WALLPAPER_DIR" -maxdepth 1 -type f \
  \( -iname '*.png' -o -iname '*.jpg' -o -iname '*.jpeg' \
     -o -iname '*.webp' -o -iname '*.gif' -o -iname '*.bmp' \) )

if [ ${#images[@]} -eq 0 ]; then
  echo "No images found in $WALLPAPER_DIR" >&2
  exit 1
fi

chosen="${images[RANDOM % ${#images[@]}]}"

swww img "$chosen" \
  --transition-type grow \
  --transition-duration 2 \
  --transition-fps 60

It scans ~/Pictures/wallpaper for common image formats, picks one at random using bash's $RANDOM, and tells swww to animate to it with a grow transition. You can change --transition-type to fade, wipe, outer, or others — run swww img --help to see the full list.

Wiring it into Hyprland

swww needs its daemon running before you can set wallpapers. Start it at login alongside an initial wallpaper pick in your Hyprland config:

exec-once = swww-daemon && sleep 0.5 && bash ~/.local/bin/rotate-wallpaper.sh

The sleep 0.5 gives the daemon a moment to initialize before the first swww img call.

Rotating on unlock with hypridle

The interesting part is triggering the rotation on unlock rather than on a timer. hypridle's lock_cmd runs when the session locks — so if you run hyprlock there and rotate the wallpaper after it returns (which is after unlock), you get a fresh wallpaper every time you come back.

There's a NixOS-specific wrinkle: hypridle runs commands via /bin/sh, which has a minimal PATH. Tools like bash, hyprlock, and swww aren't in it. You need to either hardcode Nix store paths or use a wrapper script that sets PATH explicitly.

In NixOS home-manager, pkgs.writeShellScript is the clean way to handle this:

let
  lockAndRotate = pkgs.writeShellScript "lock-and-rotate" ''
    set -euo pipefail
    export PATH=${lib.makeBinPath [
      pkgs.hyprlock pkgs.procps pkgs.swww
      pkgs.coreutils pkgs.findutils pkgs.bash
    ]}:$PATH

    # Guard against duplicate hyprlock instances
    if pidof hyprlock > /dev/null 2>&1; then
      exit 0
    fi

    hyprlock
    bash ~/.local/bin/rotate-wallpaper.sh
  '';
in

Then reference it in your hypridle config:

services.hypridle = {
  enable = true;
  settings = {
    general = {
      lock_cmd        = "${lockAndRotate}";
      before_sleep_cmd = "loginctl lock-session";
      after_sleep_cmd  = "hyprctl dispatch dpms on";
    };

    listener = [
      {
        timeout    = 300;
        on-timeout = "loginctl lock-session";
      }
      {
        timeout    = 600;
        on-timeout = "hyprctl dispatch dpms off";
        on-resume  = "hyprctl dispatch dpms on";
      }
    ];
  };
};

The flow is:

  1. After 5 minutes idle, loginctl lock-session fires
  2. That triggers lock_cmd — the lockAndRotate script
  3. hyprlock runs and blocks until the user authenticates
  4. Once unlocked, rotate-wallpaper.sh picks a new wallpaper

The duplicate-instance guard (pidof hyprlock) prevents a second lock from spawning if the session is already locked (e.g. a manual Super+L followed by the idle timeout firing).

Installing swww on NixOS

Add it to your home packages:

home.packages = with pkgs; [
  swww
];

Or if you're using home-manager modules, it's just a package — there's no dedicated module for swww.

Put your wallpapers somewhere

The script defaults to ~/Pictures/wallpaper. Any flat folder of images works. I don't bother with subdirectories since the find only goes one level deep (-maxdepth 1), which keeps it fast.

That's it. Run sudo nixos-rebuild switch --flake /etc/nixos#$(hostname), log out and back in for hypridle to start as a systemd user service, and you'll get a new wallpaper every time you unlock.

Sync the Wallpapers

I'm running a multi-host NixOS setup so I want the same wallpapers on my laptop and my desktop without a bunch of work on my part. I looked to Syncthing for this by pointing it at my wallpaper folder on both machines and sycning them through my server.