Documentation / Theming

Theming

Aphotic generates its color palette from whatever wallpaper is active and applies it consistently across the desktop — terminal, shell, editor, browser. Nothing here is a static colorscheme file you hand-edit; the palette is derived from the image.

Aphotic was previously known as Noctis-Hypr; the CLI, theme folders, and every command below are the current, renamed project.

How it works

Color generation runs through wallust, invoked with wallust run <image> against the active wallpaper. Configs/wallust/wallust.toml sets check_contrast = true globally — wallust’s own fix for a real bug where a dark/desaturated wallpaper region could otherwise generate an ANSI color with barely any contrast against the background (dim-gray color8 was the actual offender), which made things like ls output or zsh-autosuggestions nearly unreadable. This applies to every palette generation automatically, no per-theme opt-in needed.

matugen is the second colour engine, picked per theme with [engine].name = "matugen". Where wallust produces the 16 ANSI slots, matugen produces real Material You 3 roles, and the shell uses those roles directly. Both engines write the same output files, so switching a theme between them changes how the colours are chosen, not which apps get themed. Every apply path (the CLI, Settings → Appearance, the wallpaper picker, Super+W, aphotic scheme set) reads the key; any other name warns and falls back to wallust. None of the eight shipped themes pin matugen. See themes/THEME_SPEC.md in the repo for the full comparison.

What gets themed

Wallust’s generated palette is applied live to:

  • Kitty terminal
  • Hyprland — window borders (~/.config/hypr/colors.lua)
  • Quickshell — the entire shell: bar, launcher, notifications, OSD, lock screen, session menu, dashboard
  • The Aphotic greeter (login screen) — its own palette and wallpaper snapshot, resynced on every change (aphotic greeter sync)
  • Kvantum (Qt apps) and swaylock
  • Cava audio visualizer
  • Firefox — requires the Pywalfox extension; without it, Firefox theming does nothing
  • VS Code — requires the Wal Theme extension (bundled and set as the active color theme automatically by install.sh); it updates live off wallust’s own ~/.cache/wal/colors/colors.json output. If a fresh install doesn’t pick up a change immediately, reload the VS Code window once — its file watcher needs ~/.cache/wal/ to already exist when it starts
  • GTK3 apps (~/.config/gtk-3.0/gtk.css) and GTK4/libadwaita apps. libadwaita only reads its stylesheet at startup, so a theme change re-deploys it and restarts GTK4 apps that are idle; aphotic theme refresh-gtk does the same by hand

Themes vs. wallpapers vs. schemes

A theme is a directory of wallpapers plus a theme.toml manifest under ~/.config/awww/<name>/ (Configs/awww/<name>/ in the repo). The directory name is the theme’s identity — there’s no separate registry file, the filesystem is the registry. Eight ship out of the box: Gruvbox, Nordic, Rosé Pine, Tokyo Night, Catppuccin Latte, Lofi, HackTheBox, Windows 11 — each with its own curated wallpaper set, a matching Papirus folder-icon accent, and (for Latte) a light-mode variant.

~/.config/awww/<name>/
├── theme.toml
├── wallpaper-one.jpg
└── wallpaper-two.png

theme.toml can be a single line ([theme] display_name = "Lofi") — Aphotic falls back to wallust.toml’s global defaults and the alphabetically-first wallpaper in the folder. A fuller manifest can pin things per-theme:

[theme]
display_name = "Tokyo Night"
description = "Neon purples and blues, city-at-night palette."

[engine]
name = "wallust"        # "wallust" | "matugen"
backend = "fastresize"  # wallust -b: full | resized | wal | thumb | fastresize
palette = "kmeans"      # wallust -p: salience | ansi | kmeans
colorscheme = "some-name"  # fixed palette instead of image-derived — mutually exclusive with backend/palette
style = "light"         # wallust -S: dark | light — omit for dark (every theme but Latte)

[icons]
papirus_color = "green"     # one of `papirus-folders --list`
icon_theme = "Papirus-Dark"
cursor_theme = "Bibata-Modern-Ice"

[gtk]
theme = "adw-gtk3-dark"

[wallpaper]
default = "wallpaper-one.jpg"

Every [engine] field is a pin, not a requirement — a theme only needs one when its wallpapers need something different from the global default (a low-contrast pastel wallpaper might pin palette = "ansi" because kmeans picks muddy clusters on it). None of the 8 shipped themes currently use a fixed colorscheme — HackTheBox briefly did, but its dynamically-derived look was preferred, so all 8 currently derive from their image. The mechanism itself is real and tested (Configs/wallust/colorschemes/<name>.json, pywal format), just unused for now.

[wallpaper].default is only which image shows when you switch into the theme the first time — Aphotic remembers whichever wallpaper you last had active per-theme (~/.local/state/aphotic/theme.json) and resumes there afterward, not default.

matugen themes use scheme (matugen -t, e.g. scheme-vibrant) and contrast (--contrast, -1 to 1) instead of backend/palette/colorscheme; style applies to both.

Icon/cursor/GTK pins ([icons].icon_theme/cursor_theme, [gtk].theme) only apply while you haven’t manually overridden that setting in Settings → Personalization — the moment you pick one yourself, theme switching stops touching it permanently (there’s no UI to reset that flag). The folder-icon accent ([icons].papirus_color) is different: real per-icon recoloring isn’t possible with a normal icon theme since each icon’s colors are baked into its file, so this instead swaps Papirus’s folder icons between ~16 preset colors via papirus-folders — it’s always applied when set, independent of the manual-override flags above. That call writes under /usr/share/icons/, so it needs passwordless sudo to run automatically from a theme switch; without it, the step just warns and no-ops instead of blocking on a password prompt.

A scheme is a lighter switch: aphotic scheme set -n <name> re-runs the same wallust generation against whatever wallpaper is currently active, without changing the theme or wallpaper itself.

CLI reference

aphotic theme list                   # list theme folders
aphotic theme set <theme-name>       # apply a theme (its declared default, or last-used, wallpaper)
aphotic theme next / prev            # cycle themes — same as Super+./,
aphotic scheme set -n <scheme-name>  # re-apply a named color scheme to the current wallpaper
aphotic wallpaper -f <path>          # set a specific wallpaper (must live under a theme folder)
aphotic wallpaper --random           # pick a random wallpaper from the active theme
aphotic wallpaper --next             # advance to the next wallpaper in the active theme, in order
aphotic theme list --remote          # browse themes available for download
aphotic theme download <name>        # download a community theme from the remote index
aphotic theme update <name>          # refresh a downloaded theme's files from the repo
aphotic theme update --all           # refresh all downloaded (non-core) themes
aphotic theme remove <name>          # delete a downloaded (non-core) theme

Switching themes/wallpapers from the CLI, the launcher’s ~ mode, or Settings → Appearance all go through the same apply path, so state (~/.local/state/aphotic/theme.json) and every downstream sync stay consistent regardless of which one you used. Applying a theme or wallpaper:

  1. Sets the wallpaper via awww (with a wipe transition).
  2. Regenerates the palette with the theme’s engine: wallust (or a fixed colorscheme if pinned) or matugen.
  3. Fires every enabled plugin’s on_theme_change hook with the resolved palette as JSON (see Supported Features for the plugin system).
  4. Applies the folder-icon accent and any icon/cursor/GTK pins that haven’t been manually overridden.
  5. Persists theme/wallpaper state and syncs the login screens to match: the Aphotic greeter’s palette and wallpaper, and SDDM’s background (see below).

Quickshell picks up the new palette live via Colours.qml’s file watch — no reload needed.

Wallpaper sets

Each theme curates its own committed wallpaper set directly in the repo (under 20MB total across all eight), enough to keep a fresh git clone small even on a slow connection. There is no separate opt-in wallpaper pool — each theme’s wallpapers ship with it.

Picking a wallpaper

Super + W opens the picker over your desktop, showing the wallpapers in the active theme. Arrow keys move, Enter applies, Esc puts back what you had. The wallpaper under the cursor previews live on the real desktop once you stop moving, rather than on every card you pass, because each apply re-runs the colour engine.

Three layouts, chosen under Settings → Appearance → Wallpaper Picker:

Layout Shape
Coverflow The default. A rotating carousel of the active theme’s wallpapers, wrapping endlessly in both directions.
Grid A screenful at a time, across every theme rather than just the active one. The one to use with a large collection.
Dock A deck along the bottom edge that magnifies under the pointer, with the full-resolution wallpaper filling the screen behind it.

Thumbnails are cached to disk at ~/.cache/aphotic/wallpaper-thumbs the first time a wallpaper is shown, so a large collection scrolls without re-decoding full-size images. The cache is keyed on each file’s path, size and modification time: edit a wallpaper in place and it re-renders, and two themes can ship a wallpaper with the same filename without colliding. Delete the directory to force a rebuild.

Super + Ctrl + W browses every theme’s wallpapers in the launcher instead, and Settings → Appearance → Browse all wallpapers shows the same set as a grid.

Video wallpapers

Drop an .mp4, .webm, .mov, .mkv or .m4v into any theme directory and it shows up in the picker with a video badge, alongside the images.

Neither awww nor wallust reads video, so Aphotic extracts a single frame with ffmpeg and treats that as the wallpaper: it is what gets displayed and what the palette is derived from. On a stock install, picking a video gives you that still.

Installing the Live Wallpapers plugin adds the motion, playing the video over that same frame:

aphotic plugin install live-wallpaper

Video decode runs for as long as your desktop is visible, which is why it is a plugin rather than base behaviour. It pauses on battery and whenever a window covers the workspace (both on by default, under Settings → Appearance → Live Wallpapers), falling back to the still frame underneath. Remove the plugin and your video wallpapers keep working as stills rather than disappearing.

Theme Creator

Settings → Theme Creator builds a static theme with a full palette editor: a fixed colorscheme (~/.config/wallust/colorschemes/<slug>.json, pywal format) plus a matching ~/.config/awww/<slug>/theme.toml that pins [engine].colorscheme to it. The colours stay the same whichever wallpaper you pick in it.

Community themes

Themes can be downloaded from a remote index the same way plugins are:

aphotic theme list --remote          # browse themes available for download
aphotic theme download <name>        # download a community theme
aphotic theme update <name>          # refresh a downloaded theme's files from the repo
aphotic theme update --all           # refresh all downloaded (non-core) themes
aphotic theme remove <name>          # delete a downloaded (non-core) theme

Core themes (the eight that ship with the repo) cannot be removed. Community themes are stored alongside core themes under ~/.config/awww/ and follow the same theme.toml contract.

Thunar and login screen integration

Thunar has a right-click Set as Theme action for building a theme straight from an image in $HOME/Pictures (avoid special characters at the front of the path).

Every theme/wallpaper change also resyncs the Aphotic greeter (aphotic greeter sync), so the login screen shows your current wallpaper and palette. For installs that kept SDDM, the same change fires a best-effort sync (aphotic sddm sync) that copies the current wallpaper into the SDDM login theme and points its theme.conf Background= at it, keeping the login screen’s background matched to whatever’s currently active. Like the folder-icon accent, this needs passwordless sudo to run non-interactively from an automatic theme switch (the theme directory is normally chowned to your user by install.sh, so it’s often sudo-free in practice) — without it, the sync just warns and no-ops rather than blocking on a password prompt. aphotic sddm greeting [text] sets or prints the login screen’s greeting text the same way.

See also

  • Architecture — where the theming code and Quickshell services live in the repo
  • Profiles & Layers — which profile/layer installs the theming stack
  • Supported Features — theming in context with everything else shipped
  • Agentic Workflows — an unrelated ai-layer feature family, but themed the same way as the rest of the shell