External monitors don’t expose a brightness interface to the OS the same way laptop panels do, so the standard brightness keys do nothing. The fix is ddcutil, which speaks the DDC/CI protocol over i2c directly to the monitor’s firmware.

I’m running Omarchy (Arch + Hyprland), which ships SwayOSD for on-screen indicators and includes a helper script omarchy-swayosd-brightness that drives it.

Find your i2c buses

sudo ddcutil detect

Each detected monitor will show its i2c bus number. On my triple-monitor setup they came up as buses 7, 8, and 9.

The script

Save this as ~/.local/bin/hdmi_brightness.sh and make it executable (chmod +x).

#!/usr/bin/env bash
# usage: hdmi_brightness.sh up|down|set <value>

BDEVS=(7 8 9)   # i2c buses for your 3 monitors
STEP=5          # step size in percent
cmd="$1"
val="$2"

for bus in "${BDEVS[@]}"; do
  case "$cmd" in
    up)
      ddcutil setvcp 10 + "$STEP" --bus "$bus"
      ;;
    down)
      ddcutil setvcp 10 - "$STEP" --bus "$bus"
      ;;
    set)
      ddcutil setvcp 10 "$val" --bus "$bus"
      ;;
  esac
done

current_brightness=$(ddcutil getvcp 10 --bus "${bus}" 2>/dev/null | awk -F'current value =|,' '/current value/ {gsub(/ /,"",$2); print $2}')
omarchy-swayosd-brightness ${current_brightness}

VCP code 10 is the standard DDC/CI feature code for brightness. After applying the change, the script reads back the current value from one monitor and passes it to omarchy-swayosd-brightness so SwayOSD shows the indicator on screen.

Update BDEVS to match the bus numbers from ddcutil detect on your machine.

Hyprland keybindings

Add these to your Hyprland config (e.g. ~/.config/hypr/hyprland.conf):

Omarchy already binds XF86MonBrightnessUp and XF86MonBrightnessDown for laptop panel brightness. Without the unbind, both the old and new bindings fire — I noticed the SwayOSD indicator was appearing twice per keypress, which was the giveaway. Unbind first, then rebind to the ddcutil script.

unbind = , XF86MonBrightnessUp
unbind = , XF86MonBrightnessDown
bind = , XF86MonBrightnessUp,   exec, ~/.local/bin/hdmi_brightness.sh up
bind = , XF86MonBrightnessDown, exec, ~/.local/bin/hdmi_brightness.sh down

If your keyboard doesn’t have dedicated brightness keys, use a custom combo instead:

bind = SUPER, F2, exec, ~/.local/bin/hdmi_brightness.sh down
bind = SUPER, F3, exec, ~/.local/bin/hdmi_brightness.sh up

Reload Hyprland (hyprctl reload) and the keys should immediately control all three monitors in sync, with a SwayOSD brightness bar on screen.