How it works
┌─────────────────────┐
│ wallpaper_cycle.sh │ Timer (every 30 min)
│ dark/ or light/ │ Picks folder based on system appearance
└────────┬────────────┘
│ desktoppr
▼
┌─────────────────────┐ ┌──────────────────┐
│ wallpaper-faded │ │ colors.sh │──▶ SketchyBar
│ (Swift daemon) │ │ border_colors │──▶ JankyBorders
│ animated transitions│ ┌────────────────┐ │ wallpaper.conf │──▶ Kitty
└─────────────────────┘ │ wallpaper_colors│───▶│ wallpaper.toml │──▶ WezTerm
│ .py + wcsync/ │ │ wallpaper.toml │──▶ Alacritty
WatchPaths trigger ──────▶│ │ │ wallpaper.conf │──▶ Ghostty
│ │ │ wallpaper.itermcolors │──▶ iTerm2
│ │ │ wallpaper.conf │──▶ tmux
│ │ │ wallpaper.theme │──▶ btop
└────────────────┘ │ nvim_colors.lua │──▶ Neovim
│ flavor.toml │──▶ Yazi
│ starship.toml │──▶ Starship
│ wallpaper.json │──▶ OpenCode
│ hydrotodo.json │──▶ HydroToDo
└──────────────────┘
- Capture the current wallpaper per display, with fallback for dynamic wallpapers.
- Extract the dominant palette (median-cut quantization, default 8 colors) and generate a coherent color scheme.
- Write target configs atomically, reload supported apps, and skip unchanged wallpapers via perceptual hash (~370ms).
Configuration
All options live in ~/.config/wallpaper-colors/config.toml. The file is optional — defaults are used when absent.
[general]
display = 1 # Which display to extract from
n_colors = 8 # Palette size for quantization
[scheme]
min_saturation = 0.45 # Accent color saturation floor
min_value = 0.55 # Accent color brightness floor
# accent_override = "#3a7bd5" # Skip auto-detection
[borders]
vivify_sat = 0.65 # Border color saturation floor
vivify_val = 0.85 # Border color brightness floor
opacity = 179 # Active border opacity (0-255)
inactive_opacity = 102
[targets] # Enable/disable individual apps
sketchybar = true
borders = true
kitty = true
wezterm = true
alacritty = true
ghostty = true
iterm2 = true
tmux = true
btop = true
neovim = true
yazi = true
starship = true
opencode = true
hydrotodo = true
Targets
| App | Config generated | Reload |
|---|---|---|
| SketchyBar | ~/.config/sketchybar/colors.sh | sketchybar --reload |
| JankyBorders | ~/.config/wallpaper-colors/border_colors | IPC via borders binary |
| Kitty | ~/.config/kitty/themes/wallpaper.conf | kitten @ set-colors |
| WezTerm | ~/.config/wezterm/colors/wallpaper.toml | Config reload |
| Alacritty | ~/.config/alacritty/themes/wallpaper.toml | Config reload |
| Ghostty | ~/.config/ghostty/themes/wallpaper.conf | Config reload/restart |
| iTerm2 | ~/.config/iterm2/colors/wallpaper.itermcolors | One-time preset import |
| tmux | ~/.config/tmux/themes/wallpaper.conf | Auto source-file |
| btop | ~/.config/btop/themes/wallpaper.theme | Set color_theme=wallpaper |
| Neovim | ~/.config/wallpaper-colors/nvim_colors.lua | nvim --remote-send |
| Yazi | ~/.config/yazi/flavors/wallpaper.yazi/flavor.toml | On launch |
| Starship | ~/.config/starship.toml | On next prompt |
| OpenCode | ~/.config/opencode/themes/wallpaper.json | On launch |
| HydroToDo | ~/.config/wallpaper-colors/hydrotodo_colors.json | File watch |
Transitions
The wallpaper-faded daemon keeps a persistent overlay window. When the wallpaper changes, a random animation reveals the new image — zero flash.
| Transition | Effect |
|---|---|
| fade | Crossfade to new wallpaper |
| slide-* | Slide the old wallpaper off screen (left/right/up/down) |
| wipe-* | Directional wipe revealing new wallpaper |
| grow | Old wallpaper scales up and fades out |
Performance
| Metric | Value |
|---|---|
| Full color sync | ~1s |
| No-change skip (hash match) | ~370ms |
| Transition animation | 700ms |
| Transition detection latency | ~100ms |
| CPU at idle (daemon + poll) | negligible |