Post

Creating a custom screensaver on Omarchy

Replace Omarchy's ASCII art screensaver with a TTE-animated dashboard showing the clock, date, and weather.

Creating a custom screensaver on Omarchy

The problem

Omarchy ships with a screensaver that runs Terminal Text Effects (tte) on ASCII art from ~/.config/omarchy/branding/screensaver.txt. It looks great: random effects, fullscreen on every monitor, exits on any keypress.

I wanted something more practical in the center of the screen: the clock, today’s date, and the weather. I still wanted the TTE animations though, so I kept the same launcher and lifecycle hooks and only swapped what tte renders.

The solution

You need three scripts. Make them executable after you create them:

1
2
3
chmod +x ~/.config/omarchy/bin/omarchy-dashboard-frame
chmod +x ~/.local/share/omarchy/bin/omarchy-screensaver-mine
chmod +x ~/.local/share/omarchy/bin/omarchy-launch-screensaver

Step 1: The dashboard frame

This script prints the static text that tte will animate. Weather comes from wttr.in; override the city with SCREENSAVER_CITY (defaults to Sofia).

~/.config/omarchy/bin/omarchy-dashboard-frame:

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
CITY="${SCREENSAVER_CITY:-Sofia}"

weather=$(curl -s "wttr.in/${CITY}?format=%t+%C" 2>/dev/null || echo "--")

cat <<EOF
$(date +"%H:%M")

$(date +"%A · %d %B")

${CITY} · ${weather}
EOF

Step 2: The screensaver loop

I named mine omarchy-screensaver-mine so the stock omarchy-screensaver stays available if I want to switch back.

~/.local/share/omarchy/bin/omarchy-screensaver-mine:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#!/bin/bash

# omarchy:summary=Run Omarchy screensaver with a stable dashboard (time + weather)

screensaver_in_focus() {
  hyprctl activewindow -j 2>/dev/null | jq -e '.class == "org.omarchy.screensaver"' >/dev/null 2>&1
}

exit_screensaver() {
  hyprctl keyword cursor:invisible false &>/dev/null || true
  pkill -x tte 2>/dev/null
  pkill -f org.omarchy.screensaver 2>/dev/null
  exit 0
}

trap exit_screensaver SIGINT SIGTERM SIGHUP SIGQUIT

printf '\033]11;rgb:00/00/00\007'
hyprctl keyword cursor:invisible true &>/dev/null

tty=$(tty 2>/dev/null)

while true; do

  # One stable TTE instance per activation
  tte -i <(~/.config/omarchy/bin/omarchy-dashboard-frame) \
    --frame-rate 30 \
    --canvas-width 0 --canvas-height 0 \
    --reuse-canvas \
    --anchor-canvas c --anchor-text c \
    --no-eol --no-restore-cursor \
    --random-effect &

  tte_pid=$!

  # Monitor lifecycle (NO respawn spam)
  while kill -0 "$tte_pid" 2>/dev/null; do
    if read -n1 -t 1 || ! screensaver_in_focus; then
      exit_screensaver
    fi
  done

  wait "$tte_pid" 2>/dev/null

done

Step 3: The launcher

~/.local/share/omarchy/bin/omarchy-launch-screensaver:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#!/bin/bash

# omarchy:summary=Launch the Omarchy screensaver in the default terminal on the system with the correct font configuration.

if ! command -v tte &>/dev/null; then
  exit 1
fi

# Exit early if screensave is already running
pgrep -f org.omarchy.screensaver && exit 0

# Allow screensaver to be turned off but also force started
if omarchy-toggle-enabled screensaver-off && [[ $1 != "force" ]]; then
  exit 1
fi

# Silently quit Walker on overlay
walker -q

focused=$(omarchy-hyprland-monitor-focused)
terminal=$(xdg-terminal-exec --print-id)

for m in $(hyprctl monitors -j | jq -r '.[] | .name'); do
  hyprctl dispatch focusmonitor $m

  case $terminal in
  *Alacritty*)
    hyprctl dispatch exec -- \
      alacritty --class=org.omarchy.screensaver \
      --config-file ~/.local/share/omarchy/default/alacritty/screensaver.toml \
      -e omarchy-screensaver-mine
    ;;
  *ghostty*)
    hyprctl dispatch exec -- \
      ghostty --class=org.omarchy.screensaver \
      --config-file=~/.local/share/omarchy/default/ghostty/screensaver \
      --font-size=18 \
      -e omarchy-screensaver-mine
    ;;
  *foot*)
    hyprctl dispatch exec -- \
      foot --app-id=org.omarchy.screensaver \
      --config="$OMARCHY_PATH/default/foot/screensaver.ini" \
      -e omarchy-screensaver-mine
    ;;
  *kitty*)
    hyprctl dispatch exec -- \
      kitty --class=org.omarchy.screensaver \
      --override font_size=18 \
      --override window_padding_width=0 \
      -e omarchy-screensaver-mine
    ;;
  *)
    notify-send -u low "✋  Screensaver only runs in Alacritty, Foot, Ghostty, or Kitty"
    ;;
  esac
done

hyprctl dispatch focusmonitor $focused

Try it

Force-start the screensaver without waiting for hypridle:

1
omarchy-launch-screensaver

Press any key to dismiss.

Conclusion

The whole trick is that Omarchy’s screensaver is just a bash loop around tte inside a specially-classed terminal window. Swap the input, keep the launcher, and you get a useful at-a-glance display that still feels like the stock screensaver.

If you want the ASCII art back, point the launcher at omarchy-screensaver again or just keep both scripts and switch whenever you feel like it.

This post is licensed under CC BY-NC 4.0 by the author.