Expression is a highly customizable, time-aware wallpaper engine for Linux. It allows you to automate desktop wallpaper changes based on the current hour, randomly or evenly spread wallpapers from grouped directories, and define overrides for special times like sleep, lunch, or focus hours. Whether you want a subtle visual timetable or an expressive ambience that changes with your day, Expression makes your desktop emotionally intelligent.
"This is the best 24-hour automatic wallpaper setter you could've ever asked for." - sis
- Supports multiple wallpaper setters (swww, feh)
- 24-hour wallpaper cycling
- Set specific wallpaper on specific hour
- Set random wallpaper from a group of wallpapers for a specific hour
- Distribute wallpapers from a group evenly across the hour
- Override with special wallpaper based on a timetable (e.g., lunch, sleep)
- Per group config overrides
- Execute custom scripts on wallpaper change
- Make sure you have a supported wallpaper setter (
swww
,feh
) installed cargo
andrustup
should be installed for building the project
Clone the repository
git clone https://github.com/MidHunterX/Expression.git
cd Expression
Build the project
cargo build --release
Create config directory if it doesn't exist
mkdir -p ~/.config/expression
Copy the binary to your local/global bin directory. Be sure to have ~/.local/bin
in your PATH environment variable if you are doing the following command.
cp target/release/expression ~/.local/bin/
Create a configuration file: ~/.config/expression/config.toml
with the following content
[general]
backend = "swww"
[directories]
wallpaper = "~/Pictures/wallpaper_dir/"
Run Expression as a daemon:
expression &
Run Expression with debug logs:
RUST_LOG=debug expression
Expression uses a TOML configuration file located at:
~/.config/expression/config.toml
[general]
backend = "swww"
[directories]
wallpaper = "~/Pictures/wallpaper_dir"
[general]
# Supported backends: swww, feh
backend = "swww"
# Enable/Disable special collection feature
enable_special = true
# Way to select wallpaper from a group: random, spread
group_selection_strategy = "random"
# Command to execute on wallpaper change
# Examples:
# execute_on_change = "~/.scripts/custom_script.sh"
execute_on_change = "notify-send 'Wallpaper Changed'"
[directories]
# Default wallpaper directory
wallpaper = "~/Pictures/wallpaper_dir"
# Override special wallpaper directory (default: wallpaper_dir/special)
special = "~/Pictures/Wallpapers/Special"
[special_entries]
# Wallpaper item (entry/group) names situated inside special collection along with their corresponding hour
# These special wallpaper items always take precedence over other wallpaper items
5 = "rise and shine"
9 = "workout_motivation"
23 = "sleep_time"
Expression works by treating wallpapers as a single unit; whether it is a file or multiple files grouped into a directory.
- Entry β A single wallpaper file named after the hour (e.g.,
07.jpg
). - Group β A directory containing multiple wallpapers, named after the hour (e.g.,
07/
).
wallpaper_dir/
βββ 00.jpg # Entry for midnight
βββ 05.jpg # Entry for 5:00
βββ 07/ # Group for 7:00
βββ 21.jpg # Entry for 21:00
βββ 21/ # Group for 21:00
β βββ E.jpg
β βββ Zucc.jpg
β βββ ...
βββ 22.jpg # Entry for 22:00
βββ ...
Groups (directories) take precedence over Entries (individual files) by default. When a Group is active, a random wallpaper from within the group is selected by default.
Definition: Directory with non-numeric name
which contains Wallpaper Items (Entry or Group).
By definition, wallpaper_dir
itself is a Collection of Wallpapers as well.
wallpaper_dir/
β special/ # Special Collection
β βββ rise and shine.gif
β βββ sleep_time.jpg
β βββ workout_motivation.jpg
β
β collection_1/ # Custom Collection 1
β βββ 00/
β β βββ Austrian Painter.png
β β βββ Tiananmen Square.jpg
β β βββ who_is_in_paris.jpg
β β βββ ...
β βββ 01.gif
β βββ 02.png
β βββ ...
β
β Nature Collection by Twice/ # Custom Collection 2
β βββ ...
β
βββ 00.jpg # Entry for midnight
βββ 05.jpg # Entry for 5:00
βββ 07/ # Group for 7:00
βββ ...
Wallpaper Objects in Special Collection has the highest priority over everything.
Definition: Collection dir which has the highest priority when selecting wallpapers.
- By default
special
should be inside your wallpaper_dir - You can change its location by configuring
special
in[directories]
- Special wallpaper entries are defined in
[special_entries]
section in the format of:[hour] = "name"
- Disable special collection by setting
enable_special = false
in[general]
[general]
backend = "swww"
enable_special = true
[directories]
wallpaper = "~/Pictures/Wallpapers/24_hour/"
special = "~/Pictures/Wallpapers/Special/"
[special_entries]
5 = "rise and shine"
9 = "workout_motivation"
23 = "sleep_time"
Want a simple 24 hour timecycle wallpaper like this?
You can set up your wallpaper directory like this:
00.jpg 03.jpg 06.jpg 09.jpg 12.jpg 15.jpg 18.jpg 21.jpg
01.jpg 04.jpg 07.jpg 10.jpg 13.jpg 16.jpg 19.jpg 22.jpg
02.jpg 05.jpg 08.jpg 11.jpg 14.jpg 17.jpg 20.jpg 23.jpg
00.jpg
item is used for 00 to 01 and 23.jpg
item is used for 23 to 00
Note: numbers without preceeding 0 is valid too. For e.g.: 3.png
Renaming wallpapers numerically can get a little tedious especially when you want to change it into a different time. There's a solution for that. Since directories are also a valid wallpaper item, we can use that to our advantage.
wallpapers/
βββ 00/
β βββ Austrian Painter.png
βββ 02/
β βββ Tiananmen Square.jpg
βββ 03/
β βββ who_is_in_paris.jpg
...
Now you can freely move wallpapers between hours without worrying about filenames.
I'd like to get notified if its sleep time or its time for lunch via wallpaper. A truly non-intrusive way of communication. Since I work in a transparent terminal most of the time, the change is quite noticeable too. To do this:
- Create a directory named
special
in your wallpaper directory - Put wallpapers for
sleep_time
andlunch
items inspecial
directory
wallpaper_dir/
β special/
β βββ eating_ramyeon.jpg
β βββ sleep_time.jpg
β
βββ 00/
β βββ Austrian Painter.png
...
- Now let's configure which time to run these special wallpaper overrides.
# Add the names of item (file/dir) along with the time to your config in special_entries
[special_entries]
13 = "eating_ramyeon"
23 = "sleep_time"
Okay the sleep_time wallpaper shows up at exactly 23:00 and then that's about it.
But what if your wallpapers could become progressively sleepier as the hour passes?
Letβs set that up.
- Create a
sleep_time
directory inside the special directory, and add multiple progressively sleepy wallpapers:
wallpaper_dir/
β special/
β βββ eating_ramyeon.jpg
β βββ sleep_time/
β β βββ sleepy_1.jpg
β β βββ sleepy_2.jpg
β β βββ ...
β β βββ sleepy_10.jpg
...
- Now let's configure that specific group to spread out throughout the sleep hour. Create a file named
config.toml
inside your group (e.g.,sleep_time/
) with the following content:
[general]
selection_strategy = "spread" # Applies spread strategy to the group only
- Make sure the name of the directory (e.g.,
sleep_time
) is added to thespecial_entries
section.
[special_entries]
13 = "eating_ramyeon"
23 = "sleep_time"
And just like that, your system gently drifts into dreamland with you π
Expression is licensed under the GNU Affero GPL v3, ensuring all modifications and server-hosted versions remain open-source.
Contributions are welcome! Feel free to submit issues or pull requests.
Backstory: It's the year 2025 AD (Gregorian Calendar) and I was just a guy who was writing bash script to automate timed wallpapers with the help of swww daemon. But, this is still not what I really envisioned. Adding more features became a hassle. No more duct-taping and workarounds for datastructures! I have to re-write this into a performant compiled language so that I can have more features while being lightweight and resource efficient focusing on Laptop battery life. So, I want a language re-write which focuses on code maintainability, high performance, low resource utilization and scalability. I had one language in mind: C. Simple and Efficient. Since I have a little bit extra features in mind, datastructures, memory management and data processing could get verbose. This led me to try out Rust lang which gives similar performance while giving memory safety by default. Thus, Expression transcended into this world; A 24 hour ambient notification system disguised as a wallpaper setting program engineered to be lightweight, performant while using as little battery as possible.