EDIT: New version 0.2, more robust plus audio!
EDIT: v0.3 adds nice to mpv, makes it behave better ![]()
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-gitmpv- My little bash script
bgvsomewhere 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.mp4sets background to local videobgv https://example.com/video.mp4plays remote videobgvresets to normal background
Tips:
- In
mpv, hardware decoding is disabled by default. You might want to edit/create your~/.config/mpv/mpv.confand enable it:
If it worked out, and you started# Enable hardware decoding if available, =yes is implied. hwdecbgvin a terminal,mpvwill report back something likeUsing 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.

