Phonto: video wallpapers that decode on the GPU on Wayland and macOS
GPU-accelerated video wallpaper program for wayland compositors and macos
At a glance
- What is it?
- A small Rust program that plays video as a desktop background, routing decoding and compositing through GStreamer and EGL on Wayland, or VideoToolbox and CoreAnimation on macOS, with per-monitor assignment and yt-dlp support.
- Who is it for?
- Phonto is worth installing if you have ever wanted a video background and gave up because your compositor's wallpaper tool was a slideshow of stills. The scope is deliberately narrow: one process, one window or layer-shell surface, no daemon to babysit, and a dependency list where every crate earns its place.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 32 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Two completely different renderers behind one command
The pitch is a single sentence in the README: a GPU-accelerated video wallpaper program for Wayland compositors and macOS, written in Rust, that also supports live lockscreen wallpapers on macOS. Underneath that, the two platforms share nothing but the command line.
On Linux, phonto plays video as the desktop background through GStreamer and EGL, with decoding and rendering entirely on the GPU. The Cargo.toml shows how thin that layer is: gstreamer, gstreamer-app, gstreamer-video, gstreamer-gl, and gstreamer-gl-egl all pinned to 0.24, plus wayland-client 0.31 and wayland-protocols-wlr 0.2 for the layer-shell protocol, glutin 0.32.3 and glow 0.17.0 for the OpenGL context, and raw-window-handle 0.6.2.
On macOS, phonto takes a different route and says so plainly. It drives an `AVPlayerLayer` attached to a window sitting just below the system wallpaper level, so VideoToolbox handles decoding and CoreAnimation handles compositing. There are no external dependencies to install because it links only system frameworks: AVFoundation, CoreMedia, AppKit, and QuartzCore. The macOS side of Cargo.toml reflects that, pulling in objc2, objc2-foundation, and objc2-app-kit with a hand-picked feature list rather than anything multimedia, since the frameworks already exist on the machine.
The layering trick is what makes the macOS version work at all. A window positioned below the system wallpaper is still a real window, so it inherits the compositor's own ordering rather than fighting the desktop manager for the top of the screen. That is why icons, the dock, and the menu bar are unaffected while video plays underneath them.
The GStreamer plugin list is the real installation guide
On macOS, installing phonto is installing phonto. On Linux, the README spends a section longer than any other warning you will read in this project, and you should not skip it.
Phonto needs GStreamer runtime plugins beyond the VA-API plugin used for accelerated decoding. The README states the failure mode directly: missing demuxer or codec plugins can cause startup failures when opening MP4 or H.264 files. That is not a degraded experience, it is a program that refuses to play your wallpaper.
sudo pacman -S gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-libav gst-plugin-vaThe README explains why two of those packages are not optional. `gst-plugins-good` provides `qtdemux` for MP4 files, and `gst-plugins-bad` includes the H.264 parser used by many videos. Underneath every video wallpaper program is a demuxer that has to understand the container and a parser that has to understand the codec, and those live in packages whose names give no hint that they matter.
The second warning is about performance rather than function. Without the VA-API plugin, GStreamer falls back to software decoding and CPU usage will be significantly higher. For a wallpaper that plays continuously in the background, that is the difference between a tool you keep and a tool you uninstall after a day.
Equivalent package lists are given for Ubuntu and Debian and for Fedora. Worth noting what is not in them: no build dependencies, no compiler setup, nothing beyond the plugin stack. Phonto is packaged for NixOS, macOS with nix-darwin, Homebrew on macOS and Linux x86_64, the AUR on Arch, and crates.io through cargo install. The repository also carries flake.nix and flake.lock, so a Nix dev shell is available if you would rather not install the binary at all.
Local files, network streams, and YouTube without a browser extension
The argument forms are the simplest part of phonto, and they cover three very different sources.
A local file is a path. A network stream is handed to GStreamer directly, video-only, and GStreamer handles it natively. A YouTube URL or livestream is resolved by yt-dlp, which is the only external program phonto calls.
phonto /path/to/video.mp4
phonto https://example.com/stream.m3u8
phonto rtsp://camera.local/streamFor YouTube, phonto detects `youtube.com`, `youtu.be`, and `m.youtube.com`, with or without the https prefix, and spawns `yt-dlp -g` to extract the direct stream URL. The bare form works, so `youtu.be/dQw4w9WgXcQ` is enough. The README documents the exact invocation phonto uses, which is `yt-dlp -f <format> -g [--cookies-from-browser <browser>] [extra args] <url>`, taking the first stream URL from stdout and feeding it into GStreamer's `uridecodebin` with GPU decoding, the same pipeline used for local files.
Two details make this usable in practice rather than a party trick. YouTube requires a browser-like User-Agent to avoid 403 errors, and phonto sets it on the GStreamer pipeline automatically. And authentication is handled by passing `--cookies-from-browser` with a browser name, so a logged-in session works for age-restricted or members-only videos. Quality is controllable through `--yt-dlp-format`, accepting yt-dlp format selectors such as `bestvideo`, `best[height<=720]`, or `worst`, and arbitrary yt-dlp flags pass through `--yt-dlp-args` for cases like a SOCKS proxy.
There is one more piece of plumbing worth knowing. Phonto writes the currently selected video path to `~/.cache/phonto/current`, which lets another tool reuse the same clip after `--rand` picks one. That cache file is deliberately skipped in per-display playback mode, because there is no single current video when different monitors are showing different things.
Per-monitor pinning, and what happens to displays you leave out
By default phonto mirrors one video across every connected display, which is the behavior most people expect and most wallpaper tools never quite get right. New monitors plugged in mid-session attach automatically, and unplugging then re-plugging a monitor brings the wallpaper back without a restart.
The per-display controls are stricter than the mirroring default suggests. You run `phonto displays` to list outputs, where the first column is the native ID for the current OS. You then repeat `--display ID PATH` once for each display you want to set. The README is explicit that every display must be named explicitly and that there is no implicit default.
That last clause has a consequence worth internalizing. Displays you do not mention get no wallpaper from phonto and keep whatever the operating system is already showing there. So a partial configuration is not a partial wallpaper, it is a full stop for the monitors you left out. If you want a third screen covered, it has to appear in the command or the config file.
You can also ask one specific display for a random pick with `--display-rand ID`, which chooses from `search_paths` the same way the plain `--rand` flag does. The two flags mix freely and can be repeated.
[[display]]
id = "DELL U2723QE"
path = "~/Videos/ocean.mp4"
[[display]]
id = "eDP-1"
random = trueThe persistent version of the same assignment lives in config.toml under `[[display]]` blocks, after which running phonto with no arguments picks it up. Each block needs exactly one of `path` or `random = true`, and phonto errors out rather than guessing when neither or both are present. The README also points at an `[[alias]]` block for sharing one config across machines, since display IDs differ between a desktop and a laptop and an alias is how you keep the file portable.
One Linux-specific option rounds out the surface. `--layer` chooses which layer-shell layer to render on, with `background` as the default and `bottom`, `top`, and `overlay` available. This is the knob to reach for when your compositor draws panels that you want above or below the video.
Dumping a frame, and reading Cargo.toml for the real dependencies
There is a `dump` subcommand that writes a still frame to an image file rather than starting a wallpaper, and it quietly documents a shader path as well.
The invocation is `phonto dump PATH --out OUT.png [--at SECONDS] [--shader PATH]`. The `--out` flag is required, `--at` selects the timestamp in seconds and defaults to `0.0`, and dump works on both local files and resolved URLs. The `--shader` flag applies a GLSL fragment shader before saving, and the README restricts it to Wayland on Linux. That restriction is a good clue about the architecture: the shader runs on the OpenGL path, and the macOS path composites through CoreAnimation instead, so there is no equivalent hook there.
Cargo.toml is where the real dependency story lives, and it is short enough to read in one sitting. The cross-platform set is anyhow, env_logger, log, clap with the derive feature, serde with derive, toml 0.8, rand 0.9, and image 0.25.10 with the png feature. That list is the whole non-platform surface: argument parsing, structured config, logging, a random picker, and PNG output.
A few package-level details are worth recording. The version is 0.5.0 and the edition is 2024, which means a recent Rust toolchain, and the license field reads GPL-3.0-or-later. GitHub reports the license as GPL-3.0. Both statements are consistent with each other and the distinction is small, but the manifest wording is the one that carries the or-later permission to upgrade the terms in future versions, so it is the more useful of the two. The manifest also lists a repository and homepage URL and an exclude list covering the example GIF, target, nix, flake files, install.sh, todo.md, and the .github and .claude directories, which tells you the published crate is narrower than the working tree.
The release history is three tags in seven weeks: v0.3.2 on 2026-05-20, v0.4.0 on 2026-06-04, and v0.5.0 on 2026-06-15. The last push to the repository was 2026-09-09, and it had 418 stars with 10 forks and 6 open issues at the time of this review, which is a young project with a handful of people actively using it rather than a finished utility.
Editorial conclusion
Phonto is worth installing if you have ever wanted a video background and gave up because your compositor's wallpaper tool was a slideshow of stills. The scope is deliberately narrow: one process, one window or layer-shell surface, no daemon to babysit, and a dependency list where every crate earns its place. What you give up is the feature set that wallpaper managers built up over a decade. There is no GUI, no schedule, no playlist editor, and no way to script transitions, so changing wallpaper means running phonto again from a terminal. On Linux the GStreamer plugin situation is the real risk: install it without gst-plugins-good and gst-plugins-bad and an MP4 simply will not open, and without the VA-API plugin it will play using the CPU and quietly eat your battery. Install the packages from the README first, run phonto displays to learn your monitor names, and pin per monitor with config.toml once you are happy.
Frequently asked questions
What is Phonto used for?
Playing video as your desktop background. Phonto is a video wallpaper program for Wayland compositors on Linux and for macOS, and on macOS it also supports live lockscreen wallpapers. It plays local files, network streams such as m3u8 and rtsp URLs, and YouTube links resolved through yt-dlp.
Can you use Phonto on a PC?
On a Linux PC running a Wayland compositor, yes, and that is the primary target. macOS is supported through system frameworks rather than GStreamer. There is no Windows build described in the README, and the Homebrew tap is documented as macOS and Linux x86_64 only, so a Windows machine is not covered by any installation path the project documents.
What is the Phonto app?
A command line program written in Rust, not a desktop application with a settings window. Museslabs publishes it as phonto, with the name pronounced fon-to and taken from the Greek word for background. Everything is driven through flags and an optional config.toml, and the projects on GitHub topics include video-wallpaper, live-wallpaper, and wayland-wallpaper.
Why will my MP4 fail to play on Linux?
Almost always a missing GStreamer plugin rather than a problem with phonto or the file. The README names qtdemux in gst-plugins-good for MP4 demuxing and the H.264 parser in gst-plugins-bad, and says missing demuxer or codec plugins can cause startup failures. Install the plugin packages for your distribution before debugging anything else. If playback works but CPU usage is high, the VA-API plugin is missing and GStreamer has fallen back to software decoding.
How do I show a different video on each monitor?
Run phonto displays to get the native IDs, then repeat `--display ID PATH` for each monitor you want covered. There is no implicit default, and any display you leave out gets no wallpaper from phonto at all, which is the detail that surprises people. The same assignments can be persisted in config.toml as `[[display]]` blocks with either a path or random = true, which is the form you want if you also use `[[alias]]` to share a config between a desktop and a laptop.
Does the shader option work on macOS?
No. The README restricts the `--shader` flag on the dump subcommand to Wayland on Linux, because it applies a GLSL fragment shader on the OpenGL rendering path. On macOS phonto composites through CoreAnimation, so there is no equivalent hook and passing the flag is not supported.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/museslabs-phonto)