Per-monitor brightness, contrast, and gamma control for Hyprland — with a companion GTK4 GUI for live tweaking.
hyprgamma applies a software color ramp to each output through Hyprland's gamma LUT, letting you set brightness, contrast, and per-channel gamma independently for every monitor. Settings are saved to disk and re-applied automatically on startup and on monitor hotplug.
It's handy when:
- one of your monitors is dimmer or warmer than the others and you want them to match,
- your panel has no usable hardware brightness control (most desktop displays),
- you want a quick contrast/gamma curve without an ICC profile or redshift-style tool.
- Demo
- Requirements
- Install (hyprpm)
- Manual build
- Usage
- Configuration file
- How it works
- Troubleshooting
- Uninstall
- License
Screenshot / GIF of the GUI goes here. Drop an image at
assets/gui.pngand it will render below:
- Hyprland 0.55.x (the plugin is ABI-locked to the Hyprland it was built against — see Troubleshooting)
hyprpm(ships with Hyprland) for the easy install path- For a manual build:
cmake(≥ 3.19), a C++23 compiler, the Hyprland headers, andnlohmann_json - For the optional GUI:
gtk4andjson-glib
On Arch:
sudo pacman -S --needed base-devel cmake nlohmann-json gtk4 json-glibThis is the recommended path — hyprpm builds the plugin against your exact
Hyprland version, so it stays in sync across updates.
# add and build the plugin repo
hyprpm add https://github.com/surprizeattackxx-dotcom/hypr-gamma
# enable it
hyprpm enable hyprgammaThen make sure your plugins load on login. Add this to hyprland.conf if it
isn't there already:
exec-once = hyprpm reload -nAfter a Hyprland update, refresh the plugin so it's rebuilt against the new version:
hyprpm updateYou should see a green [hyprgamma] Loaded! notification when it starts.
Useful for development or if you'd rather not use hyprpm.
git clone https://github.com/surprizeattackxx-dotcom/hypr-gamma
cd hypr-gamma
# build the plugin
cmake -DCMAKE_BUILD_TYPE=Release -B build
cmake --build build -j"$(nproc)"
# -> build/libhyprgamma.so
# load it for the current session
hyprctl plugin load "$PWD/build/libhyprgamma.so"
⚠️ A manually built.somust be compiled against the headers of the exact Hyprland version you're running, or the plugin will refuse to load with a version-mismatch notification.hyprpmhandles this for you; manual builds are on you.
The plugin registers a set of hyprctl commands (all prefixed hyprgamma:)
and two dispatchers for keybinds. Per-monitor settings are persisted
automatically after every change.
| Command | Description |
|---|---|
hyprctl hyprgamma:list |
List every monitor with its current settings (human-readable). |
hyprctl -j hyprgamma:list |
Same, as JSON (used by the GUI / scripts). |
hyprctl hyprgamma:set <monitor> <key> <value> |
Set one parameter on a monitor. |
hyprctl hyprgamma:enable <monitor> |
Re-apply this monitor's saved curve. |
hyprctl hyprgamma:disable <monitor> |
Drop this monitor back to an identity ramp. |
hyprctl hyprgamma:reset [monitor] |
Reset one monitor (or all, if omitted) to defaults. |
hyprctl hyprgamma:save |
Force-write the config file. |
hyprctl hyprgamma:reload |
Reload the config file from disk and re-apply. |
Keys accepted by set:
| Key | Range (typical) | Meaning |
|---|---|---|
brightness |
0.0 – 2.0 |
Linear multiplier on output (1.0 = unchanged). |
contrast |
0.0 – 2.0 |
Expansion/compression around mid-grey (1.0 = unchanged). |
gamma |
0.1 – 5.0 |
Gamma exponent applied to all three channels at once. |
gamma_r |
0.1 – 5.0 |
Red-channel gamma only. |
gamma_g |
0.1 – 5.0 |
Green-channel gamma only. |
gamma_b |
0.1 – 5.0 |
Blue-channel gamma only. |
Find your monitor names with hyprctl monitors (e.g. DP-1, HDMI-A-1, eDP-1).
Examples
# dim the second monitor to 80%
hyprctl hyprgamma:set DP-2 brightness 0.8
# warm up a too-blue panel (lift red, drop blue)
hyprctl hyprgamma:set HDMI-A-1 gamma_r 0.9
hyprctl hyprgamma:set HDMI-A-1 gamma_b 1.2
# punch up contrast on the laptop screen
hyprctl hyprgamma:set eDP-1 contrast 1.15
# undo everything on every monitor
hyprctl hyprgamma:resetTwo dispatchers are exposed for bind:
# hyprland.conf
bind = $mainMod SHIFT, G, hyprgamma:reset # reset all monitors to defaults
bind = $mainMod SHIFT, R, hyprgamma:reload # reload config from disk
# bind step adjustments straight to hyprctl, too:
bind = , XF86MonBrightnessUp, exec, hyprctl hyprgamma:set eDP-1 brightness 1.1
bind = , XF86MonBrightnessDown, exec, hyprctl hyprgamma:set eDP-1 brightness 0.9A small companion app gives you live sliders for every monitor — it talks to
the plugin over hyprctl, so the plugin must be loaded for it to do anything.
cd gui
cmake -DCMAKE_BUILD_TYPE=Release -B build
cmake --build build -j"$(nproc)"
./build/hyprgamma-guiEach detected monitor gets a card with an enable toggle, brightness/contrast sliders, per-channel gamma sliders, and a Reset Monitor button. Changes apply instantly and are saved automatically.
Settings live at:
$XDG_CONFIG_HOME/hypr/hyprgamma.json # or ~/.config/hypr/hyprgamma.json
You normally never edit this by hand — the plugin and GUI manage it — but it's plain JSON if you want to version it or sync it across machines:
{
"version": 1,
"defaults": {
"brightness": 1.0,
"contrast": 1.0,
"gamma_r": 1.0,
"gamma_g": 1.0,
"gamma_b": 1.0,
"enabled": false
},
"monitors": {
"DP-1": {
"brightness": 0.85,
"contrast": 1.1,
"gamma_r": 1.0,
"gamma_g": 1.0,
"gamma_b": 1.05,
"enabled": true
}
}
}The config is loaded and applied on plugin init, and re-applied whenever a monitor is hotplugged.
For each monitor, hyprgamma builds a gammaSize × 3 lookup table and pushes it
to the output via Hyprland's setGammaLut. For a normalized input n ∈ [0,1]
on each channel:
out = clamp( ( pow(n, gamma) - 0.5 ) * contrast + 0.5 ) * brightness , 0, 1 )
A disabled monitor is given a plain identity ramp, so toggling enabled is a
lossless on/off without discarding your saved values. Outputs that report a
gamma table smaller than 10 entries (i.e. no real gamma support) are skipped.
[hyprgamma] Version mismatch notification / plugin won't load.
Hyprland plugins are tied to the exact compositor build. Run hyprpm update to
rebuild against your current Hyprland, or recompile a manual build against the
matching headers.
A command returns error: gamma manager not initialized.
The plugin didn't finish loading. Check hyprctl plugin list and your startup
notification.
Nothing happens on a particular monitor. That output may not expose a usable gamma LUT (some virtual/remote/HDR-pipeline outputs don't). hyprgamma silently skips outputs with no gamma support.
Colors look washed out at high brightness.
brightness > 1.0 multiplies values and will clip highlights — that's expected.
Use it to brighten dim panels, not to push past 1.0 on an already-bright one.
hyprpm disable hyprgamma
hyprpm remove hyprgammaFor a manual load, just unload it: hyprctl plugin unload "$PWD/build/libhyprgamma.so".
BSD-3-Clause © donnie