MOK's Onomatomaniske Kaos
Super Cool

The walpal utility for creating color themes


This is the writeup of a new utility I have written, inspired by Pywal. You can find it on Codeberg.


This project is heavily inspired by Dylan Araps’ Pywal application. Dylan archived the github repository in April 2024, it has since been forked by github user eylles and expanded for 16 colors in the project Pywal16.

However I found that Pywal16 gave me two sets of identical colors, not 16 unique. It seemed color1 and color8 where identical, color2 and color9, and so on. I tried examining the source code to find out why, but honestly I had trouble understanding it. So I decided to write my own walpaper analysing program that does not rely on external tools, but relies solely on Python modules.

Walpal is a lot simpler and limited than Pywal and it also works in a somewhat different way. Rather than analysing the background image on the fly, walpal must first preprocess the image to determine the palette of dominant colors as the first step in a two stage process. It goes on to save the color palette in the user’s ~/.cache directory. In the following, I will use the words “palette” and “theme” interchangeably. It depends on whether we’re talking about a set of RGB colors, or if it’s something you activate to change the appearance of the screen.

Once the dominant colors of a background image has been generated, walpal can activate the palette similar to what Pywal does, and walpal can do this very quickly.

I found this two stage process to not be a practical problem, as all it takes is to run the preprocessing on the directory containing images, e.g. /usr/share/backgrounds/.

Images are read using the Python Imaging Library (pillow), that is capable of reading a great number of different image formats. However, walpal expects images to containmeaning three channel RGB or four channel RGBA data that also includes a transparency layer.

Images are then analysed by the Kmeans algorithm in the scikit-learn module. The palette data is further saved in a Pandas dataframe, which is saved in the user’s ~/.cache area in the portable Apache Parqet format, and contains rgb, hls and luminocity data for each color. Additionally the palette colors are saved in a json file for easy access.

1 Preprocessing images

It is possible to preprocess a single image using the --image switch:

$ walpal --image van-gogh-starry-night.jpg
[INFO] Processing van-gogh-starry-night

2 Preprocessing a directory

You can preprocess all images in a directory in one go.

$ walpal --directory /usr/share/backgrounds/linuxmint-wilma
[INFO] Preprocessing /usr/share/backgrounds/linuxmint-wilma
[INFO] Processing mpiwnick_atacama
[INFO] Processing jcorl_eclipse
[INFO] Processing ztasi_moon_phases
[INFO] Processing meiying_body_of_water
[INFO] Processing mfakurian_abstract
[INFO] Processing mnohassi_morocco
[INFO] Processing jcorl_white_sands
[INFO] Processing aksenapati_snow_mountain
[INFO] Processing mpiwnicki_torres_del_paine
[INFO] Processing pblache_colors
[INFO] Processing kwinegeart_road
[WARNING] Not an image file: Credits
[INFO] Processing jcorl_monument_valley
[INFO] Processing mpiwnicki_palm
[INFO] Processing aksenapati_blanket
[INFO] Processing slee_blocks
[INFO] Processing ztassi_england
[INFO] Processing jpanchal_cpu
[INFO] Processing mpiwnicki_sunset
[INFO] Processing mnohassi_ocean
[INFO] Processing slee_earth
[INFO] Processing mpiwnicki_chalet
[INFO] Processing mnohassi_boat

From now on, the palettes (or “themes”) are identified by their file name without extension, like it’s listed above.

All the information walpal gathers while processing the images are stored in the file ~/.cache/walpal/walpal.db. If you delete this file it will automatically be regenerated, but walpal will forget all information about what images have been processed and will not be able to activate the themes, so you will have to preprocess the background images again. If you want walpal to forget about a theme, use the --delete option (see below). The walpal.db file is a Python shelve file, but it’s actually in sqlite format so you can also manipulate it with sqlite3 if you know how.

3 Activating a theme

To activate a theme, simply call walpal with the --palette option, and as argument use the palette name:

$ walpal --palette slee_earth

If you forget what themes/palettes you have available, they can be listed with the --list switch:

$ walpal --list
mpiwnick_atacama
jcorl_eclipse
ztasi_moon_phases
meiying_body_of_water
...

mpiwnicki_chalet
mnohassi_boat

NB: If you need to output the list of themes, for example in a shell script, this construct can be useful:

themes=$(walpal --list)

When you activate a theme, walpal finds the current theme and optionally sends control characters to all open terminals changing the ANSI colors 1. That’s all it does by default, if you need it to do more, see the following sections.

The change of colors are not persistent, however, and they can be reset to their defaults using the teminal initialization command reset (see reset(1)). If you want the theme colors to be persistent in the terminal, you need to do that via templates, see below.

If you do not want walpal to touch the terminal colors, use the --no-tty flag.

Running scripts

As we saw above, waypal only generates the color palette and temporarily sets terminal colors. However, when walpal runs, it will look for scripts to run in the directory ~/.config/walpal/scripts/. The scripts need to have the extension .sh and they need to be executable. They are executed in globbing order, if you want to be systematic about it, you can name them with leading numerals like this: 10-set-wallpaper.sh, then they will be executed in that order. The aforementioned script might look like this:

#!/usr/bin/bash
source $HOME/.cache/walpal/current-theme.sh
swww img --transition-type any "$WALLPAPER"

It illustrates the use of the file current-theme.sh that walpal creates whenever it activates a theme and stores in the .cache/walpal/ directory. The content of this file is very simple:

# Generated by walpal 2025-05-22 17:42 CEST
THEME="slee_earth"
WALLPAPER="/usr/share/backgrounds/linuxmint-wilma/slee_earth.jpg"

As an inspiration, here is another useful script called 50-waybar.sh that you can use to restart waybar so it uses the active theme, more on how to accomplish that in the next section.

#!/usr/bin/bash
pkill waybar
waybar &

Templates

When walpal activates a theme, it does several tings. In the directory ~/.cache/walpal/ it:

  • Open the database walpal.db to see if it knows the theme.
  • Reads the pregenerated parquet file and generates a color dictionary from it.
  • Saves this dictionary in colors.json.
  • Saves a CSS file with the current color palette in colors.css.
  • Creates the aforementioned shell source file current-theme.sh

The next thing walpal does when activating a theme is to generate theme files for other applications, and places them in your directory ~/.cache/walpal.

The templates must be found in your config directory ~/.config/walpal/templates/. The template files must be in Jinja2 format. OBS This is different from the template format used in Pywal. The important difference if you want to port your Pywal templates is that variables are inclosed in double braces, like this {{ color3 }}. Here is an example for use with waybar:

@define-color foreground {{ foreground }};
@define-color background {{ background }};
@define-color cursor {{ cursor }};
@define-color color0 {{ color0 }};
@define-color color1 {{ color1 }};

... etc

@define-color color14 {{ color14 }};
@define-color color15 {{ color15 }};

This template will be converted and placed in ~/.cache/walpal/colors-waybar.css. In other words, it retains the same name, but will expanded and placed in the ~/.cache/walpal directory. In the proper waybar style sheet, that is placed whereever waybar finds its style (~/.config/waybar/style.css). You then need to include the palette in that file with something analogous to this:

@import url("/home/mok/.cache/walpal/colors-waybar.css");

Be aware that walpal orders the colors in the palette according to their luminocity. That means that when it generates a dark theme (option --dark) the darkest colors will be color1, color2, etc, and the lightest colors will be color13, color14 and color15. When generating a light theme, the ordering of colors is reversed, so the light colors have the lowest numbers and the dark colors have … 13, 14, 15. The background and foreground colors are chosen from the darkest and lightest colors, but made a bit darker and a bit lighter, depending whether the theme is dark or light.

If you would like to see and study the palette, you can run walpal with the --display= flag, which generates an overview image and calls you system image display program to show it. If you want to save the image, you can do it from there. If you want to study the hexcodes from the pallete, you can find the json format files for all themes that have been activated in the directory ~/.cache/walpal/palettes.

Finally, after all you templates have been processed, walpal runs all scripts found in ~/.config/walpal/scripts like described above. Of course this means that anything done by these scripts is done with the current, last activated theme.

4 Installing walpal from source

If you’ve come this far, you probably already know what to do, but just in case, to download the source code of walpal, clone it using git:

git clone https://codeberg.org/mok0/walpal.git

Next, if you haven’t already, you need to install the Python build module:

pip install build

then cd into the walpal source directory and:

python -m build --wheel
pip install dist/walpal-0.8-py3-none-any.whl

walpal requires the following external packages:

  • pandas
  • numpy
  • jinja2
  • pillow
  • scikit-learn

but pip will automatically fetch and install them for you.

Pip will install walpal as an executable script, you may have to modify your PATH environmental variable for the shell to find it.

5 Command line options

walpal --help
usage: __main__.py [-h] [--image IMAGE] [--directory DIRECTORY] [--light]
                   [--dark] [--force] [--quiet] [--no-tty] [--debug] [--list]
                   [--palette PALETTE] [--display] [--delete DELETE]
                   [--version]

walpal -- Generate colorschemes from background images

options:
  -h, --help            show this help message and exit
  --image, -i IMAGE     Preprocess single background image
  --directory, -d DIRECTORY
                        Preprocess image directory
  --light               Generate a light colorscheme.
  --dark                Generate a dark colorscheme (default).
  --force               Force (re)preparation of image.
  --quiet, -q           Be quiet.
  --no-tty, -t          Be quiet.
  --debug               Output debug info.
  --list                List all information.
  --palette, -p PALETTE
                        Set active palette.
  --display             Show the active palette.
  --delete DELETE       Delete palette from database.
  --version             Print walpal version and exit.


  1. This piece of code is adopted from Pywal with thanks. ↩︎