Bgv – Script for Desktop Background Video

EDIT: New version 0.2, more robust plus audio!
EDIT: v0.3 adds nice to mpv, makes it behave better :wink:
EDIT: v0.4 only cosmetic changes (indentation, comments)
EDIT: v0.5 adds opacity for subtle overlays, see below

I couldn’t resist making a small script that allows the desktop background to be a video (video is better than GIF—it can use hardware acceleration):

You need

  • X11 (I don’t have Wayland, maybe it’s possible…)
  • xwinwrap-git
  • mpv
  • My little bash script bgv somewhere in the path (~/bin, /usr/local/bin, …)

The script:

#!/usr/bin/env bash

# Use xwinwrap and mpv to display a background video.
# 2025-06-05 Moonbase59

# Based on https://www.youtube.com/watch?v=b8rh9m3wOjk
# Use `yay -S xwinwrap-git` to install (takase1121 version).
#
# Note: `mpv` defaults to hardware decoding _disabled_. Enable in
# `~/.config/mpv/mpv.conf` like so:
#   # Enable hardware decoding if available, =yes is implied.
#   hwdec

# v0.1 -- Initial version.
# v0.2 -- Added audio feature, help, version.
# v0.3 -- Added `nice` to mpv command, bg video doesn’t need NI=0.
# v0.4 -- Cosmetic: Uniform indentation, more comments.
# v0.5 -- 2025-06-08 Added opacity (0.0..1.0)

me=$(basename "$0")
version="0.5"

# set defaults
AUDIO="--no-audio"
VOLUME="0"
OPACITY="1.0"

# check if xwinwrap & mpv installed
command -v xwinwrap >/dev/null 2>&1 || { echo >&2 "$me requires \"xwinwrap\" but it's not installed. Aborting."; exit 2; }
command -v mpv >/dev/null 2>&1 || { echo >&2 "$me requires \"mpv\" but it's not installed. Aborting."; exit 2; }

# parse arguments
_usage() {
cat <<EOF
Usage: ${me} [OPTIONS] [VIDEO]

${me} sets a background video for the X11 desktop.
Uses xwinwrap (yay -S xwinwrap-git) and mpv.

Options:
  -h, --help     Show this help.
  -o, --opacity  Opacity of background video, default=1.0 [0.0..1.0]
  -V, --version  Show version number of this script.
  -v, --volume   Volume of background video, default=0 [-1..100]

Video:
  (none)         Stop ${me}, revert to previous desktop background.
  /path/to/file  Set local video file as background.
  URL            Set video at URL as background; requires Internet.
                 You can use YouTube URLs.
EOF
}

PARSED_ARGUMENTS=$(getopt -a -n "${me}" -o ho:Vv: --long help,opacity:,version,volume: -- "$@")
VALID_ARGUMENTS=$?
if [ "$VALID_ARGUMENTS" != "0" ]; then
  _usage
  exit 2
fi

eval set -- "$PARSED_ARGUMENTS"
while :
do
  case "$1" in
    -h | --help)
      _usage
      exit 0
      ;;
    -o | --opacity)
      OPACITY="$2"
      shift 2
      ;;
    -V | --version)
      echo "$me $version"
      exit 0
      ;;
    -v | --volume)
      VOLUME="$2"
      AUDIO="--volume=$VOLUME"
      shift 2
      ;;
    # -- means the end of the arguments; drop this, and break out of the while loop
    --)
      shift
      break
      ;;
    # If invalid options were passed, then getopt should have reported an error,
    # which we checked as VALID_ARGUMENTS when getopt was called...
    *) echo "Unexpected option: $1 - this should not happen."
       _usage
       exit 2
       ;;
  esac
done

PIDFILE="/var/run/user/$UID/$me.pid"
declare -a PIDs

# Set up display: $1=geometry, $2=opacity, $3=audio option, $4=file/URL
# mpv WILL output status. That is desired for diag.
# Close terminal or use autostart if you don’t want that.
_screen() {
  xwinwrap -fdt -b -ni -s -nf -g "$1" -o "$2" -- \
    nice mpv --fullscreen \
    --no-stop-screensaver \
    --loop-file --no-osc --no-osd-bar \
    --wid=%WID --no-input-default-bindings \
    $3 \
    "$4" &
  PIDs+=($!)
}

# Remove previously started background video(s)
# PIDFILE might not exist on first run
if [[ -e "$PIDFILE" ]]; then
  while read p; do
    ## SIGTERM is usually good enough, use SIGKILL (-9) only if needed.
    [[ $(ps -p "$p" -o comm=) == "xwinwrap" ]] && kill "$p";
  done < $PIDFILE
fi

sleep 0.5

# Set up background video on connected display(s)
if [[ -n "$1" ]]; then
  for i in $( xrandr -q | grep ' connected' | grep -oP '\d+x\d+\+\d+\+\d+' )
  do
    _screen "$i" "$OPACITY" "$AUDIO" "$1"
  done
  printf "%s\n" "${PIDs[@]}" > $PIDFILE
else
  rm $PIDFILE
fi

Usage:

  • bgv /path/lo/local/video.mp4 sets background to local video
  • bgv https://example.com/video.mp4 plays remote video
  • bgv resets to normal background

Tips:

  • In mpv, hardware decoding is disabled by default. You might want to edit/create your ~/.config/mpv/mpv.conf and enable it:
    # Enable hardware decoding if available, =yes is implied.
    hwdec
    
    If it worked out, and you started bgv in a terminal, mpv will report back something like
    Using hardware decoding (vaapi).
    
  • You can put your bgv … command in the autostart if you want. Use 10s delay or so.
  • It’s best to play a short local video loop (say, a minute or so), ideally H.264, since that can be hardware-decoded on almost any platform and saves CPU.
  • Playing Youtube videos works (no sound, intentionally) but probably doesn’t make too much sense to play 10 hour fireplace videos… Requires permanent Internet connection in this case.
  • Be aware that playing a non hardware-decodeable video format can eat a lot of CPU!

If you are a Wayland guru, you might modify the script to work with Wayland. Many people would like that.

Would be interesting to see the comparison of system resource usage with and without bgv running.

Seems reasonable. I’m on EOS/Cinnaomon and watched htop for a while:

  • load average ~0.50, memory 3.15 GB while running
  • load average ~0.47, memory 2.79 GB when not running

Used a “local” (NAS actually, pulled over NFS) 1280x720 H.264 video (the “Moonbase” broadcast break loop).

hidamari seems working on wayland.

That’s good to know. I just wanted to learn more about the inner workings, and not really install a fully-blown GUI app. I think there a many more things apart from mpv that could be done. And I plan to stick with X11 for the foreseeable future.

If it doesn’t break, why switch? If you don’t game on your system, X11 is perfectly fine.

Exactly.

Just remember, that “foreseeable future” may be more seeable than you know. X11 will soon be gone. Well, relatively soon. The writing is on the wall…

I’ve never been particularly dazzled by video wallpapers, but this is so simple and light on resources I’m gonna give it a shot! Thanks for sharing :smiley:

Just for the fun of it: Live View from the ISS:

bgv "https://www.youtube.com/watch?v=xRPjKQtRXR8&pp=ygUQc3BhY2VzaGlwIGZsaWdodA%3D%3D"

Note: Live streams sometimes generate a few errors.

I’d guess wrong variant of xwinwrap? There is only one that has adapted to the new mpv argument format. Check:

$ pacman -Qs xwinwrap
local/xwinwrap-git r20.539fc47-1
    Fork of XwinWrap from takase1121 on GitHub

Unfortunately, all xwinwrap versions in the AUR have slight differences and quirks.

I could have installed the wrong one. I’ll try again when I get a chance.

I also updated the script to v0.2. It has help now and can handle audio as well (default=audio off).

yeah I apparently picked the wrong one in pacseek. I played a short video ( as in time not format) and it auto looped so that was cool however exiting the process (ctrl + c) it still played the video in the background not necessarily unwanted but it does kill the menu for openbox. I had to kill it through killall mpv. Not a deal breaker and I like the simplicity of it. I’m still on v0.01 I haven’t copied the updated yet

Cool. Took me 4 installs to finally get the “right” xwinwrap. It’s a mess… :rofl:

It should actually continue playing. Sometimes the prompt doesn’t appear, just press Enter. Giving an “empty” bgv without options or filename should stop it playing.

The audio I added because a friend wanted to hear his fireplace video crackling at a lower volume. Tell ya, never start such things… :smile: People playing 10 hour relaxation videos might also like the new feature, maybe. :wink:

Example of v0.2 playing a YT video, sound volume at 80%, and me doing some upgrades:

Interesting experiment: Started a live cam stream

bgv "https://ch-fra-n9.livespotting.com/vpu/8h9zgq58/pyb3snyw_2160.m3u8?"

(Norden-Norddeich, Germany)

Then closed laptop lid. Goes into suspend. Open it again, and it even resumed the live cam stream. How cool is that?

X11 may have a future yet:

I expect it’d probably also depend how the video was encoded, and the resources required to decode it.

Like if it’s H.264 encoded to a Baseline profile, it’s going to require very little CPU resources at least, to decode. Or there’s something like HAP, specifically designed for high performance playback with reduced CPU overhead (leverages GPU).