Run GLSL shaders as your Linux wallpaper.
neowall_demo.mp4
neowall_mouse.mp4
Paste a Shadertoy shader into a file, point neowall at it, get an animated desktop. Wayland + X11, multi-monitor, single static binary, no daemon-in-Python anywhere.
neowallIt pauses itself the moment a window covers the wallpaper, so it isn't burning your battery while you actually use the computer.
# Arch (AUR)
yay -S neowall-bin # prebuilt, tracks each release
yay -S neowall-git # builds from git
# Prebuilt binary
# https://github.com/1ay1/neowall/releases/latest
tar -xzf neowall-linux-x86_64-*.tar.gz
sudo install -m755 neowall /usr/local/bin/
# From source
git clone https://github.com/1ay1/neowall && cd neowall
meson setup build && ninja -C build
sudo ninja -C build installBuild dependencies
# Debian/Ubuntu
sudo apt install build-essential meson ninja-build libwayland-dev \
libgles2-mesa-dev libpng-dev libjpeg-dev wayland-protocols \
libx11-dev libxrandr-dev libxkbcommon-dev
# Arch
sudo pacman -S base-devel meson ninja wayland mesa libpng libjpeg-turbo \
wayland-protocols libx11 libxrandr libxkbcommon
# Fedora
sudo dnf install gcc meson ninja-build wayland-devel mesa-libGLES-devel \
libpng-devel libjpeg-turbo-devel wayland-protocols-devel \
libX11-devel libXrandr-devel libxkbcommon-devellibxkbcommon is optional at build time, but without it the terminal
wallpaper compiles fine and then silently has no keyboard input — install it.
~/.config/neowall/config.vibe
default {
shader retro_wave.glsl
shader_speed 0.8
}
Image slideshow:
default {
path ~/Pictures/Wallpapers/
duration 300
transition glitch
}
Per-output:
output {
DP-1 { shader matrix_rain.glsl }
HDMI-A-1 { path ~/Pictures/ duration 600 }
}
Full reference: config/docs/CONFIG.md.
neowall # start
neowall kill # stop
neowall reload # reload config
neowall next # next wallpaper / shader
neowall pause # pause cycling
neowall resume
neowall pause-shader # freeze the shader animation in place
neowall resume-shader # resume a frozen shader animation
neowall list # show queue
neowall set 3 # jump to index
neowall current # print what's playingWriting a shader:
neowall new aurora # scaffold a working shader and tell you how to run it
neowall new hud --template=reactive # or: scene (default), flat, reactive
neowall watch aurora.glsl # live-reload as you edit
neowall preview aurora.glsl # run it once, without touching your wallpaper30+ in the box. Bring your own — neowall accepts unmodified Shadertoy GLSL: drop a .glsl file into ~/.config/neowall/shaders/ and reference it from your config.
| Vibe | A few |
|---|---|
| Synthwave | retro_wave · synthwave · neonwave_sunrise |
| Nature | moon_ocean · living_aurora · sunrise |
| Cyber | matrix_rain · matrix_real · glowing_triangles |
| Abstract | fractal_land · plasma_touch · mandelbrot |
| Space | star_next · starship_reentry |
Pair it with GLEditor for a live-preview shader workflow that one-clicks into neowall.
neowall is a superset of Shadertoy: on top of full compatibility it feeds
shaders live data from your machine and ships a GLSL std-lib (noise, SDFs,
palettes, tonemap) injected automatically — no #include. Shaders can react to
system audio (FFT), CPU/RAM/network load, battery, the real time of day, and
your mouse/keyboard. See docs/REACTIVE_SHADERS.md.
| Shader | Reacts to |
|---|---|
audio_pulse · audio_bars |
system audio — spectrum, bass, beats |
mouse_ripples |
water ripples from your cursor (click = splash) |
plasma_touch |
plasma you push around with the mouse |
fireflies |
a swarm that gathers at your pointer, flares on beat |
starfield_warp |
warp speed scales with mouse motion + audio |
system_monitor |
CPU heat, RAM tide, network pulses, battery |
system_deck |
a command-deck HUD: CPU/RAM gauges, scrolling history graph, per-core meters, net traces, battery, clock |
rain_window |
rain on glass — heavier with network, lightning on beat |
circadian_sky |
a sky synced to your real clock: dawn → noon → dusk → stars |
Drop a .neowall sidecar next to any shader for explicit channel bindings and
custom reactive uniforms (e.g. uniform uGlow audio_bass). Audio capture uses
parec (PipeWire/PulseAudio) and degrades to silence if it's not installed. It
records your speaker output (the default sink's monitor), never the
microphone, and only starts when the loaded shader actually reads audio.
Changing what feeds iChannel0..3 (audio, noise, self-feedback, another
buffer, a texture, a bitmap font atlas): a bare .glsl guesses via a heuristic;
a .neowall sidecar lets you bind each channel explicitly — channel0 audio,
channel1 self, channel2 font, etc.
See docs/REACTIVE_SHADERS.md.
Any terminal program can be the wallpaper. neowall ships its own in-tree,
dependency-free VT/xterm-class terminal emulator (real PTY, spec-correct
ANSI/DEC parser, cell grid with SGR truecolour, scroll regions, the alternate
screen, mouse reporting and colour emoji) and renders its cell grid on the GPU —
so btop, htop, cava, vim or a scrolling journalctl -f run live behind
your windows.
default {
terminal btop
term_cols 0 # 0 = auto-fit the whole display
term_rows 0
}
default {
terminal "journalctl -f"
term_font_size 16
term_shader crt.glsl # optional: pipe the terminal through a CRT/glow pass
}
It's a real terminal, not a screenshot: it answers the startup handshake (cursor-position report, Device Attributes) so probing TUIs enter their render loop and animate, forwards the pointer when the app enables mouse reporting, and auto-restarts the command if it exits. No libvterm, no external terminal — every byte of the parser and screen model is in-tree, in the same spirit as neowall's hand-rolled event loop and config parser.
Full key reference (term_font, term_font_size, term_fg/term_bg,
term_cwd, term_env, term_shader, …) is in
config/docs/CONFIG.md.
Single C binary. The event loop is built on timerfd + signalfd — there is no polling thread anywhere. Rendering goes through EGL → OpenGL 3.3 (desktop) with a GLES 2.0 fallback for older GPUs.
config.vibe → event loop ─┬─→ EGL / OpenGL 3.3
├─→ Wayland (layer-shell)
└─→ X11 (root window + EWMH)
Smart pause is the main thing that separates neowall from the obvious one-evening hack:
- Wayland:
wl_surface.framewatchdog +wlr-foreign-toplevelstate bits, plus a Hyprland-specific IPC path that handles the awkward "tiled mosaic of small windows covers the wallpaper" case - X11: EWMH
_NET_WM_STATE_FULLSCREENon every output
Result: while a fullscreen game, a maximized browser, or a wall of tiled terminals is covering the screen, neowall draws zero frames. The moment you switch workspaces, it picks back up.
| neowall | swww | mpvpaper | hyprpaper | |
|---|---|---|---|---|
| Animated GLSL shaders | ✓ | – | – | – |
| Drop-in Shadertoy support | ✓ | – | – | – |
| Terminal / TUI as wallpaper | ✓ | – | – | – |
| Image slideshow | ✓ | ✓ | – | ✓ |
| Video | – | gifs only | ✓ | – |
| Wayland | ✓ | ✓ | ✓ | ✓ |
| X11 | ✓ | – | – | – |
| Pauses when occluded | ✓ | – | – | – |
If you want video wallpapers, use mpvpaper — neowall is deliberately not that.
- KDE Plasma: native desktop icons may hide while neowall owns the background layer. Use a dock or
plasma-desktop's widgets. - GNOME/Mutter and other compositors without layer-shell: unsupported. Wayland does not allow a client to map a visible wallpaper from a bare
wl_surface; neowall exits with an actionable error instead of silently selecting a non-visible fallback. - No video: by design.
meson setup build --buildtype=debug
ninja -C build
./build/neowall -f -vArchitecture, threading model, and contribution conventions are documented in
docs/ARCHITECTURE.md. Run the test suite (headless,
no display server needed) with meson test -C build; for the memory-safety and
concurrency gates use a sanitized build:
meson setup build-asan -Db_sanitize=address,undefined && meson test -C build-asan
meson setup build-tsan -Db_sanitize=thread && meson test -C build-tsan output_refcountShaders, bug reports, and compositor coverage PRs are all welcome. Issue tracker is the place.
MIT.