From 2def1a3e09f1e6a113627652b4ed74ec551cc2d1 Mon Sep 17 00:00:00 2001 From: Richard Zink Date: Tue, 22 Sep 2026 11:27:15 +0200 Subject: [PATCH] feat: add ash-wallpaper tool for generating ember-highlight wallpapers --- README.md | 15 +++++++++++ tools/ash-wallpaper.sh | 59 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100755 tools/ash-wallpaper.sh diff --git a/README.md b/README.md index 408273d..d6e2623 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,21 @@ No wallpapers are shipped (Harbor's don't match the palette). Drop images into `~/.config/omarchy/themes/ash-dark/backgrounds/` and cycle with `Super+Ctrl+Space`, or keep them in `~/.config/omarchy/backgrounds/ash-dark/`. +### Generating Ash wallpapers from your own photos + +`tools/ash-wallpaper.sh` (ImageMagick) turns any color photo into an Ash +wallpaper: the image is reduced to Ash-tinted greys, while warm ember/gold +highlights (sunset, fire, lights, skin tones) are selectively preserved. + +```sh +tools/ash-wallpaper.sh photo.jpg # -> photo-ash.png, dark base +PRESET=light tools/ash-wallpaper.sh photo.jpg # light variant base +``` + +Tuning via environment variables: `HUE_MAX`/`HUE_MIN` (width of the warm hue +band), `SAT_MIN`/`SAT_MAX` (how colourful a pixel must be to survive), +`EMBER_BOOST` (saturation of highlights), `BASE_LO`/`BASE_HI` (grey ramp). + ## Regenerating from the palette The files in `ash-light/` and `ash-dark/` are generated from diff --git a/tools/ash-wallpaper.sh b/tools/ash-wallpaper.sh new file mode 100755 index 0000000..b023955 --- /dev/null +++ b/tools/ash-wallpaper.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +# ash-wallpaper.sh — turn a color photo into an Ash-palette wallpaper: +# grayscale body tinted toward the Ash tones, with warm "ember" highlights +# (reds/oranges/golds) selectively preserved. Pleasantville-style. +# +# usage: tools/ash-wallpaper.sh INPUT [OUTPUT] +# +# tuning via environment (defaults shown): +# PRESET=dark dark (void->chalk) | light (stone->paper) +# HUE_MAX=13% warm band: hue <= HUE_MAX +# HUE_MIN=94% ... or hue >= HUE_MIN (catches pure reds) +# SAT_MIN=12% below this saturation pixels lose the highlight +# SAT_MAX=45% at/above this saturation the highlight is full +# EMBER_BOOST=130 saturation multiplier (%) applied to highlight areas +# SENSITIVITY=8x45% sigmoidal contrast of the final mask +# BASE_LO= BASE_HI= override the grayscale tint endpoints +set -euo pipefail + +IN=${1:?usage: ash-wallpaper.sh INPUT [OUTPUT]} +OUT=${2:-${IN%.*}-ash.png} +PRESET=${PRESET:-dark} +HUE_MAX=${HUE_MAX:-13%} +HUE_MIN=${HUE_MIN:-94%} +SAT_MIN=${SAT_MIN:-12%} +SAT_MAX=${SAT_MAX:-45%} +EMBER_BOOST=${EMBER_BOOST:-130} +SENSITIVITY=${SENSITIVITY:-8x45%} + +if command -v magick >/dev/null; then IM=magick; else IM=convert; fi + +case "$PRESET" in + dark) BASE_LO=${BASE_LO:-#100f12}; BASE_HI=${BASE_HI:-#f3ede1} ;; + light) BASE_LO=${BASE_LO:-#b7ae98}; BASE_HI=${BASE_HI:-#f2ecdf} ;; + *) echo "PRESET must be dark or light" >&2; exit 1 ;; +esac + +TMP=$(mktemp -d) +trap 'rm -rf "$TMP"' EXIT + +# warmth mask: (hue in warm band) x (saturation ramp) +"$IM" "$IN" -colorspace HSL -channel R -separate "$TMP/hue.png" +"$IM" "$IN" -colorspace HSL -channel G -separate "$TMP/sat.png" +"$IM" "$TMP/hue.png" -threshold "$HUE_MAX" -negate "$TMP/low.png" +"$IM" "$TMP/hue.png" -threshold "$HUE_MIN" "$TMP/high.png" +"$IM" "$TMP/low.png" "$TMP/high.png" -compose Lighten -composite "$TMP/band.png" +"$IM" "$TMP/sat.png" -level "$SAT_MIN","$SAT_MAX" "$TMP/satmask.png" +"$IM" "$TMP/band.png" "$TMP/satmask.png" -compose Multiply -composite \ + -gaussian-blur 0x2 -sigmoidal-contrast "$SENSITIVITY" "$TMP/mask.png" + +# ash base + ember-touched highlights, joined by the mask +"$IM" "$IN" \ + \( -clone 0 -modulate 100,0 +level-colors "$BASE_LO","$BASE_HI" \) \ + \( -clone 0 -modulate "100,$EMBER_BOOST" \) \ + -delete 0 \ + \( +clone "$TMP/mask.png" -alpha off -compose CopyOpacity -composite \) \ + -delete 1 -compose Over -composite \ + -colorspace sRGB "$OUT" + +echo "wrote $OUT"