Skip to content

scootbg overview

The lightest wallpaper daemon for Wayland, in place of swaybg, hyprpaper, wpaperd and friends. It shows a color or an image on each output, and one command changes it. Built for scoot and set up by scoot’s own config, but not tied to it: scootbg speaks only standard protocols, so it also runs on any compositor with wlr-layer-shell-v1.

Terminal window
scootbg set '#1e1e2e' # every output, including ones plugged in later
scootbg set ~/Pictures/hills.jpg # an image, covering every output
scootbg set ~/Pictures/hills.jpg --output DP-2 --mode fit
scootbg clear # back to the compositor's own background
scootbg query # each output and what it shows, one JSON line

In scoot, a [wallpaper] section runs all of this for you: scoot starts scootbg itself and re-applies the section on every reload, with no [autostart] entry and no session script. Leave scootbg daemon out of [autostart] and your session script when you use this section (apply-config starts the daemon; a second start is harmless but flashes the wrong profile’s wallpaper for a moment).

[wallpaper]
image = "~/Pictures/hills.jpg" # or: color = "#1e1e2e"
mode = "fill" # fill | fit | stretch | center | tile
[wallpaper.output."DP-2"] # optional, one table per output
color = "#101014"

A link works where a path does: image = "https://example.com/hills.jpg" is downloaded once and cached by scootbg, so an example look can name an image nobody commits. sha256 pins the download’s bytes.

Field Type Reload Meaning
image string (path or URL) live (re-applied) A PNG, JPEG or WebP image. A path: ~ expands against HOME, a relative path resolves against the config file’s directory. A URL (http:///https://): downloaded once and cached — see Wallpaper from a link.
color string ("#rrggbb") live (re-applied) A solid color. image or color, never both; neither is nothing (the compositor’s own background_color).
mode string live With an image only: fill (cover and crop), fit (letterbox with fill), stretch, center, tile.
fill string ("#rrggbb") live With an image only: the color around a fit or center image.
filter string live With an image only: lanczos3, catmull-rom, bilinear or nearest.
sha256 string (64 hex digits) live (re-downloads) With a URL image only: the download’s expected SHA-256. Anything else fails instead of showing. Refused beside a path.
output."NAME" table live The same keys for one output, by connector name (as scootbg query lists them). Each output table stands alone: an output’s image does not take the top level’s mode. An empty table is nothing on that output.
command string live The scootbg to run. Default "scootbg" on PATH; the Nix modules set it to the installed package’s store path.

Whichever you changed last wins. Edit [wallpaper] (and start or reload scoot): the config’s wallpaper shows. Run scootbg set after that: your pick shows, and keeps showing across restarts and unrelated reloads, until you next change [wallpaper] itself.

When it fails, the session carries on with its background_color: scootbg not installed (a warning naming the command; the next reload tries again), a failed run (exit status in the log), or a section with a problem (unknown key, wrong type — the rest of the file still applies, unlike other tables). Until scootbg’s first frame, scoot shows its background_color.

On a compositor with wp_single_pixel_buffer_manager_v1 and wp_viewporter (scoot, sway, and most others) a color costs no shared memory at all: one single-pixel buffer scaled to the output. Without single-pixel buffers it is a 1×1 wl_shm buffer under the viewport, and without a viewporter a full-size buffer. A static color asks for no frame callbacks and wakes the daemon for nothing.

Once on screen a wallpaper costs no CPU (no wakeups in a minute, measured) and one buffer per output at most: about 4.0 MB RSS with a color, 36.8 MB with an image on a 4K output, and the same 36.8 MB with two 4K outputs showing it. The measured budget is in README.md, and so is the comparison with swaybg, awww, wbg and wpaperd on one machine. It is not yet the lightest on every row: awww holds 1.0–1.6 MiB less idle memory above the output buffers. scripts/scootbg-bench/bench.py re-runs the comparison.

One daemon per display: its socket is $XDG_RUNTIME_DIR/scootbg-NAME.sock, NAME being the last component of $WAYLAND_DISPLAY. A second scootbg daemon refuses while one runs, and a socket left by a dead one is replaced. It exits 0 on kill and 1 when the compositor goes away, removing its socket either way. A signal (SIGTERM, Ctrl-C) ends it on the spot and leaves the socket file; the other commands then say no daemon is running (exit 1), and the next scootbg daemon replaces the file. Every command exits 2 on a usage error (an unknown command or argument, or a bad --profile name).