Query each adapter's actual limits via adapter.limits() instead of the conservative downlevel_defaults(), and drop the redundant surface reconfigure on Suboptimal (the texture is still valid to present).
gpupaper
Wayland layer-surface wallpaper runner powered by GLSL/WGSL fragment shaders, built on wgpu.
Runs shaders full-screen on one or all monitors as a proper compositor layer surface (via zwlr-layer-shell-v1). Supports Vulkan and OpenGL backends, with automatic fallback for drivers that misbehave at device creation (e.g. Mesa V3DV on Raspberry Pi).
Features
- GLSL and WGSL shaders — use
.frag/.glslfor GLSL or.wgslfor WGSL - ShaderToy-compatible GLSL — shaders using
mainImage(out vec4, in vec2)work without modification - glpaper-compatible GLSL — shaders using
void main()andgl_FragColorwork as-is - Multi-monitor support — target a specific output by name or use
allto run on every connected display simultaneously - Hotplug — new outputs are picked up automatically when using
all - Vulkan-first with GL fallback — prefers discrete Vulkan, falls back gracefully to integrated or GL
- Layer selection — place the shader on any compositor layer:
background,bottom,top, oroverlay - FPS cap — optionally limit frame rate; defaults to vsync
Shader uniforms
The following uniforms are available in all shaders:
| Uniform | Type | Description |
|---|---|---|
time |
float |
Seconds since program start |
resolution |
vec2 |
Output dimensions in pixels |
mouse |
vec2 |
Mouse position (currently 0,0) |
WGSL shaders receive these via a uniform buffer:
struct Uniforms {
time: f32,
_pad: f32,
resolution: vec2<f32>,
mouse: vec2<f32>,
}
@group(0) @binding(0) var<uniform> u: Uniforms;
GLSL shaders can declare them as bare uniform variables — gpupaper strips those declarations and injects the UBO automatically, so existing ShaderToy-style shaders need no changes.
Usage
gpupaper <output> <shader_file> [--fps <n>] [--layer <layer>]
| Argument | Description |
|---|---|
<output> |
Wayland output name (e.g. HDMI-A-1) or all / * |
<shader_file> |
Path to a .frag, .glsl, or .wgsl shader file |
--fps <n> |
Target frame rate; 0 = vsync (default) |
--layer |
Compositor layer: background (default), bottom, top, overlay |
Examples:
# Run on all outputs, vsync
gpupaper all /usr/share/gpupaper/shaders/wave.frag
# Run on a specific monitor, capped at 30 fps
gpupaper DP-1 ~/shaders/my_shader.frag --fps 30
# Run on the bottom layer (above wallpaper managers that use background)
gpupaper all /usr/share/gpupaper/shaders/fireflies.frag --layer bottom
Output names can be found with wlr-randr, hyprctl monitors, or wayland-info.
Bundled shaders
A collection of example shaders is installed to /usr/share/gpupaper/shaders/:
black_hole · building · cloud · cloudninja · cubes · fireflies · firering ·
flags · fluid_spiral · green_lines · light_line · light_wave · matrix · mickey ·
rainbow_donut · rainbow_lights · rainbow_ring · rainbow_triangle · red_lines ·
slinky · snake_torus · space · spiral · torus · trippy_cube · tunnel ·
twisty · water · wave · white_hole · yellow_lines
Installation
Arch Linux (AUR)
Binary package for x86_64 and aarch64 (no build dependencies required):
yay -S gpupaper
Or build from source:
yay -S gpupaper-git
From source
Dependencies: Rust stable, libwayland, libxkbcommon, pkg-config
cargo build --release
./target/release/gpupaper --help
Compatibility
| Platform | Backend | Notes |
|---|---|---|
| AMD / Intel (desktop) | Vulkan | |
| NVIDIA | Vulkan | Requires nvidia-utils |
| Raspberry Pi 4/5 | OpenGL (EGL) | V3DV Vulkan driver skipped automatically |
| Most other hardware | Vulkan or GL | Whichever wgpu can initialise first |
Requires a Wayland compositor that implements zwlr-layer-shell-v1 (Sway, Hyprland, river, labwc, and most wlroots-based compositors).
Acknowledgements
Inspired by glpaper by Scoopta. gpupaper extends the concept with multi-monitor support, WGSL, a wgpu backend, and compatibility with both glpaper-style and ShaderToy-style GLSL shaders.
License
MIT