Complete reference for configuring NeoWall.
Edit ~/.config/neowall/config.vibe:
default {
shader matrix_real.glsl
shader_speed 1.0
}
Save and changes apply automatically.
- User config:
~/.config/neowall/config.vibe - System config:
/etc/neowall/config.vibe(fallback)
Config is auto-created on first run with Matrix rain as default.
Simple bracket-based format:
# Comments start with #
section {
key value
another_key value
}
- No quotes needed for simple strings
- No colons or semicolons
- Whitespace flexible
- Braces show hierarchy
Applies to all monitors unless overridden by output.
default {
shader matrix_real.glsl
shader_speed 1.0
mode fill
}
Override settings for specific monitors:
output {
eDP-1 {
shader matrix_real.glsl
}
HDMI-A-1 {
path ~/Pictures/wallpaper.png
}
}
Get monitor names:
- Hyprland:
hyprctl monitors - Sway:
swaymsg -t get_outputs - Generic:
wlr-randr
Run GPU shader as wallpaper:
shader matrix_real.glsl # From ~/.config/neowall/shaders/
shader ~/custom/shader.glsl # Absolute path
shader /usr/share/shaders/x.glsl # System path
Included shaders:
matrix_real.glsl- Matrix rain with detailmatrix_rain.glsl- Classic Matrix effectplasma.glsl- Flowing plasma wavesaurora.glsl- Northern lights2d_clouds.glsl- Procedural cloudssunrise.glsl- Dynamic skyfractal_land.glsl- Fractal landscapesmandelbrot.glsl- Mandelbrot zoom- And more in
~/.config/neowall/shaders/
Control shader animation speed:
shader_speed 1.0 # Normal (default)
shader_speed 2.0 # 2x faster
shader_speed 0.5 # Half speed
shader_speed 0.1 # Very slow
Only affects shaders, not images.
Static image wallpaper:
path ~/Pictures/wallpaper.png # Single file
path ~/Pictures/Wallpapers/ # Directory (cycles all images)
Supported formats: PNG, JPEG/JPG
Directory mode:
- Add trailing slash:
~/Pictures/Wallpapers/ - Loads all PNG/JPEG files
- Cycles alphabetically
- Use with
durationto auto-cycle
How image fills screen:
mode fill # Scale to fill, crop if needed (default, recommended)
mode fit # Scale to fit, may show borders
mode center # No scaling, center image
mode stretch # Stretch to fill (may distort)
mode tile # Repeat image as tiles
Seconds between wallpaper changes:
duration 300 # 5 minutes
duration 900 # 15 minutes
duration 1800 # 30 minutes
duration 3600 # 1 hour
duration 0 # No cycling (default)
Works with:
- Image directories (
path ~/Pictures/Wallpapers/) - Shader directories (
shader ~/.config/neowall/shaders/)
Effect when changing wallpapers:
transition fade # Smooth crossfade (default)
transition slide_left # Slide left
transition slide_right # Slide right
transition glitch # Digital glitch
transition pixelate # Mosaic blocks
transition none # Instant switch
Only applies to image cycling, not shaders.
Transition length in seconds:
transition_duration 0.3 # Default
transition_duration 0.1 # Fast
transition_duration 5 # Smooth
transition_duration 10 # Slow
Randomise the order of a directory cycle. Works for both image directories and shader directories.
shuffle true # Random order, fresh permutation every wrap
shuffle false # Alphabetical order (default)
- Applied at startup so each launch is a different sequence.
- Re-applied every time the cycle wraps, so a long-running daemon doesn't settle into a fixed loop. The just-shown wallpaper is held back from position 0 on the new pass so the same item never appears twice in a row.
- All monitors that share a config block see the same shuffled order, so multi-monitor synchronised cycling keeps working.
- Saved cycle position (the bookmark used to resume where you left off after
a daemon restart) is disabled under
shuffle true— the saved index refers to a different random permutation than the one you'd get on the next launch, so resuming at it is meaningless.
Run any terminal program as the wallpaper. neowall has its own in-tree,
dependency-free VT/xterm-class terminal emulator — a real PTY running the
command, a spec-correct ANSI/DEC parser, and a cell grid (SGR truecolour,
scroll regions, alternate screen, mouse reporting, colour emoji) rendered on the
GPU. TUIs like btop, htop, cava and vim run live behind your windows.
Names the command launched under the PTY. Mutually exclusive with path and
shader.
terminal btop # a bare command
terminal "journalctl -f" # quote if it has arguments
terminal "htop -d 10"
The command runs via $SHELL -c, so pipelines and arguments work. If it exits,
neowall relaunches it after a short backoff (so a transient crash resumes the
wallpaper instead of freezing on the last frame).
The terminal grid in character cells. 0 (the default) means auto-fit the
whole display from its resolution and the font's cell size.
term_cols 0 # auto-fit width (default)
term_rows 0 # auto-fit height (default)
term_cols 120 # or pin an explicit grid
term_rows 40
Cell font size in pixels. Larger = bigger text, fewer cells when auto-fitting.
term_font_size 16 # default is a mid-size cell
term_font_size 24 # chunkier, fewer rows/cols
Paths to TrueType/OpenType font files. If unset, neowall picks a sensible monospace face it finds on the system. Bold/italic fall back to the regular face synthesised if not given.
term_font ~/.fonts/JetBrainsMono-Regular.ttf
term_font_bold ~/.fonts/JetBrainsMono-Bold.ttf
term_font_italic ~/.fonts/JetBrainsMono-Italic.ttf
Colour emoji fonts (CBDT/CBLC or sbix bitmap strikes, e.g.
NotoColorEmoji.ttf) are detected automatically and rendered in colour.
Hex colours for the default foreground and background (cells that don't set
their own via SGR). term_bg also sets the surface behind the text.
term_fg #d0d0d0
term_bg #101018
Directory the command starts in (defaults to inherited cwd).
term_cwd ~/projects
The value of $TERM seen by the child (default xterm-256color).
COLORTERM=truecolor is always exported so 24-bit colour works.
term_env xterm-256color
Optional GLSL shader that post-processes the rendered terminal (a CRT curve,
glow, scanlines, …). The shader samples the terminal via nwTerm().
term_shader crt.glsl
shader_fps and vsync also apply in terminal mode (they pace the redraw).
The built-in terminal path renders the cell grid procedurally on the GPU, so it
can add effects a real terminal can't. Each key is a 0.0–1.0 intensity;
omit a key to use its built-in default. Set to 0 to disable an effect.
| Key | Effect | Default |
|---|---|---|
term_fade |
Change-driven pulse: a cell briefly glows when its content updates, then settles — graphs and numbers glide instead of snapping. | 0.5 |
term_bloom |
Brightness-keyed glow: bright/bold cells bleed light; dim text stays crisp. | 0.35 |
term_scanline |
Faint phosphor scanline locked to the cell rows (never moires with pixels). | 0 (off) |
term_crt |
Gentle CRT barrel warp + vignette. | 0 (off) |
term_chroma |
Per-channel chromatic aberration that grows toward the screen edges. | 0 (off) |
default {
terminal btop
term_font_size 18
# a full retro-CRT look:
term_bloom 0.5
term_scanline 0.4
term_crt 0.6
term_chroma 0.4
}
A custom term_shader samples nwTerm() (the crisp grid, no effects). The
built-in effects live in nwTermFX(), which a styling shader may also call.
These sit at the top level of config.vibe, outside any default {} or
output {} block. They configure the daemon process as a whole, not a single
wallpaper surface.
Whether the wallpaper surface receives pointer events.
mouse_interaction true # default — pointer enters wallpaper, iMouse fed to shaders
mouse_interaction false # wallpaper is invisible to the pointer
When false:
- Wayland:
wl_pointeris not bound (or is released if already bound). The compositor routes pointer events to whatever is underneath the wallpaper. - X11:
XQueryPointerpolling and Button/Motion events are skipped. - Shaders:
iMousestays at its initial fallback (screen center). - No themed cursor is set on the wallpaper.
Use cases for turning it off:
- You don't want neowall to override the cursor theme over the wallpaper.
- Your shader doesn't use
iMouseand you want to avoid the per-motion lock traffic from pointer events. - Principle: a wallpaper shouldn't grab input focus.
Automatically pause rendering when the wallpaper is covered:
pause_on_fullscreen true # Pause rendering (default)
pause_on_fullscreen false # Keep rendering behind covering windows
Saves GPU/CPU when the wallpaper isn't visible (fullscreen games, videos, maximized apps).
Works per-output — if only one monitor is covered, the others keep rendering.
What triggers a pause:
- A fullscreen window on the output
- A maximized window on the output
- The compositor stops requesting frames for the wallpaper surface (500ms watchdog)
- On Hyprland: tiled/floating windows that together cover ≥
pause_coverage_thresholdof the output (via Hyprland's IPC socket; default 80%)
Fraction of the wallpaper region that tiled windows must cover before the output counts as occluded. Only consulted on Hyprland (other compositors don't expose the geometry needed to compute this).
pause_coverage_threshold 0.8 # Default — pause at 80% coverage
pause_coverage_threshold 0.95 # Conservative — only pause when nearly fully tiled
pause_coverage_threshold 0.5 # Aggressive — pause as soon as half is tiled
Range: 0.0 to 1.0. The wallpaper region excludes Hyprland's reserved
zones (waybar etc.), so those don't count toward "uncovered wallpaper".
Compositor support:
- Hyprland: full coverage detection (fullscreen, maximized, tiled mosaic via IPC)
- Sway / River / other wlroots: fullscreen + maximized (via
wlr-foreign-toplevel-management) + frame-callback watchdog - KDE Plasma (KWin): frame-callback watchdog (pauses when KWin throttles obscured surfaces)
- GNOME (Mutter): frame-callback watchdog (pauses when Mutter throttles obscured surfaces)
- Other / unknown Wayland: frame-callback watchdog (best-effort)
- X11: Any EWMH-compliant window manager (i3, bspwm, dwm, etc.)
default {
shader matrix_real.glsl
shader_speed 1.0
}
default {
path ~/Pictures/wallpaper.png
mode fill
}
default {
path ~/Pictures/Wallpapers/
duration 300
transition fade
mode fill
}
default {
path ~/Pictures/Wallpapers/
duration 300
shuffle true
transition fade
mode fill
}
output {
eDP-1 {
shader matrix_real.glsl
}
HDMI-A-1 {
path ~/Pictures/monitor.png
mode fill
}
}
default {
shader ~/.config/neowall/shaders/
duration 600
shader_speed 1.0
}
default {
terminal btop
term_cols 0 # auto-fit the whole display
term_rows 0
term_font_size 16
}
With a CRT styling pass and a scrolling log:
default {
terminal "journalctl -f"
term_shader crt.glsl
term_fg #33ff66
term_bg #001100
}
default {
shader plasma.glsl
shader_speed 0.5
}
output {
eDP-1 {
shader matrix_real.glsl
shader_speed 2.0
}
HDMI-A-1 {
path ~/Pictures/Wallpapers/
duration 300
transition fade
}
DP-1 {
path ~/Pictures/static.png
mode fit
}
}
Don't mix these in the same section:
Images vs Shaders vs Terminal:
- Use exactly one of
path,shader, orterminalper section - Use
mode/transitionwith images, not shaders or terminals - Use
shader_speedwith shaders, not images term_*keys apply only withterminalshader_fps/vsyncapply to shaders and terminals (both animate)
Valid:
default {
shader matrix_real.glsl
shader_speed 1.0
duration 0
}
Invalid:
default {
path ~/Pictures/wallpaper.png
shader matrix_real.glsl # ERROR: Can't use both
}
Control running daemon:
neowall # Start daemon
neowall kill # Stop daemon
neowall next # Skip to next wallpaper/shader
neowall pause # Pause cycling (stops advancing between wallpapers)
neowall resume # Resume cycling
neowall pause-shader # Freeze the shader animation in place
neowall resume-shader # Resume a frozen shader animation (continues from the same frame)
neowall current # Show current wallpaperpause/resume stop the slideshow from advancing between wallpapers;
pause-shader/resume-shader freeze the animation of the current shader (its
time uniform) and stop drawing frames, leaving the last frame on screen. They
are independent — pausing cycling does not freeze the animation, and vice versa.
(There is no neowall reload — see Reloading Config.)
Config is read once at startup. There is no hot-reload — changes to
~/.config/neowall/config.vibe take effect on the next daemon start.
To apply a config change:
neowall kill # Stop the running daemon
neowall # Start it againOr in one line:
neowall kill && neowallCreate ~/.config/neowall/shaders/myshader.glsl:
#version 100
precision highp float;
uniform float time; // Seconds since start
uniform vec2 resolution; // Screen dimensions
void main() {
vec2 uv = gl_FragCoord.xy / resolution.xy;
vec3 color = vec3(uv, sin(time));
gl_FragColor = vec4(color, 1.0);
}Use in config:
default {
shader myshader.glsl
}
Most Shadertoy shaders work with minimal changes. NeoWall provides:
iTime- Time in secondsiResolution- Screen resolutioniChannel0throughiChannel4- Texture samplersiChannelTime[4]- Per-channel timeiChannelResolution[4]- Per-channel resolutioniMouse- Mouse position (always vec4(0))iDate- Date vectoriFrame- Frame counter (always 0)
To convert Shadertoy shader:
- Copy shader code
- Save to
~/.config/neowall/shaders/name.glsl - Use in config:
shader name.glsl
Most shaders work as-is. Some may need minor adjustments.
Browse shaders: shadertoy.com
- Neowall does not hot-reload. Restart the daemon:
neowall kill && neowall - Check:
~/.config/neowall/config.vibeexists - Check logs:
neowall -fv(foreground, verbose)
- Shaders in:
~/.config/neowall/shaders/ - Use filename only:
shader matrix_real.glsl - Or full path:
shader ~/.config/neowall/shaders/matrix_real.glsl
- Check logs:
neowall -fv - Verify GPU supports OpenGL ES 2.0+
- Try different shader:
shader plasma.glsl - Check shader syntax errors in logs
- Verify file exists:
ls -la ~/Pictures/wallpaper.png - Check format: PNG or JPEG only
- Try absolute path:
/home/user/Pictures/wallpaper.png
- Get exact name:
hyprctl monitorsorswaymsg -t get_outputs - Check spelling in config
- Monitor must be active
- Set
duration > 0 - For directories, add trailing slash:
path ~/Pictures/Wallpapers/ - Verify directory contains images:
ls ~/Pictures/Wallpapers/
- Shaders: ~2% CPU at 60 FPS (GPU accelerated)
- Static images: ~0% CPU (after load)
- Image cycling: Brief spike during transition
- Fullscreen apps: 0% CPU/GPU (auto-paused by default)
- Base: ~10-20 MB
- Per shader: +5-10 MB
- Per image: +image file size (uncompressed)
- Shaders render at 60 FPS
- Uses OpenGL ES 2.0+ for compatibility
- Automatic fallback for older GPUs
Override config location:
NEOWALL_CONFIG=~/custom/config.vibe neowallSwitch between configs:
neowall -c ~/.config/neowall/work.vibe
neowall -c ~/.config/neowall/gaming.vibeRun in foreground with verbose logging:
neowall -fvOutput shows:
- Config parsing
- Shader compilation
- Image loading
- Monitor detection
- Frame timing
- Main README:
../README.md - Example config:
../config/neowall.vibe - Shader directory:
~/.config/neowall/shaders/ - GitHub: github.com/1ay1/neowall