Note
This project is complete and stable, no actions are needed unless a driver update breaks it!
NVIDIA GPUs have a nice feature called Digital Vibrance that increases the colors saturation of the display. The option is readily available on nvidia-settings in Linux, but is too coupled with libxnvctrl, making it softly "exclusive" to the X11 display server over wayland; but I paid for my pixels to glow :^)
An interesting observation is that the setting persists after modifying it on X11 and then switching to Wayland. I theorized (1) (2) it was possible to call some shared library or interface to configure it directly in their driver, independently of the display server, and indeed, it is possible!
This repository uses nvidia-modeset and nvkms headers found at nvidia/open-gpu-kernel-modules to make ioctl calls in the /dev/nvidia-modeset device for configuring display attributes. These headers are synced with the proprietary releases, should work fine if you're on any of nvidia-dkms, nvidia-open or nvidia.
Note: A future, and intended way, will be through NVML, as evident by some nvidia-settings comments
β€οΈ Consider supporting my work, this took 26 hours to figure out, implement, write a readme, make it convenient π«
There's multiple ways to get nvibrant, do check the usage and autostarting sections afterwards!
This utility finds the best nvibrant binary for your driver, already bundled in the package for all known tags at release time. Simply install the pypi/nvibrant package, where uvx β’ tools usage is recommended:
# With standard python tooling
$ python3 -m pip install nvibrant
$ python3 -m nvibrant 512 512
# Always latest, simpler
$ uvx nvibrant 512 512For more stability, pin to a specific version and only update for new features or newer drivers support:
$ python3 -m pip install nvibrant==1.1.0
$ uvx nvibrant==1.1.0 (args)Note: This package is an official release channel for nvibrant
Install from your distro's package manager, it may use the python package at system level:
| Distro | Installation | Maintainer | |
|---|---|---|---|
|
|
Arch Linux |
Install the nvibrant-bin AUR package:
|
@Incognitux |
|
|
Nix Flakes |
Use the nix-nvibrant flake.
See repo for instructions. |
@mikaeladev |
|
|
Fedora |
Install the nvibrant rpm.
See repo for instructions. |
@ykshek |
| ... | ... |
Help me by packaging for your distro!
β€οΈ Thanks to all maintainers for your work! β€οΈ |
You π |
Obligatory: Community packages are often safe, but not always checked by me
You can download all latest builds made by GitHub Actions in the Releases page as a .tar.gz archive, just remember to run chmod +x nvibrant* to mark the files as executable after extracting!
- Run them directly as
./nvibrant-linux-amd64-<driver>-v<version>.bin (args)
Note
There is some level of compatibility across different nvibrant and driver versions, as the related code is mostly stable on nvidia's side. Always prefer using the closest, but not newer, version to your driver!
- Example: Running
nvibrant-linux-amd64-575.51.03-v1.0.6.binon driverv575.64.03works - This is automatically handled by the python utility, hence the strong recommendation :)
Requirements: Have git, gcc compilers; meson and ninja are included on python dependencies
# Clone the code alongside open-gpu-kernel-modules
git clone https://github.com/Tremeschin/nvibrant && cd nvibrant
git submodule update --init --recursiveFrom here, you can either build only the C++ part for a target driver:
# Any tag from https://github.com/NVIDIA/open-gpu-kernel-modules/tags
$ cd open-gpu && git checkout 575.64.03 && cd ..
# Configure and compile project, final binary at 'build' directory
$ meson setup --buildtype release ./build && ninja -C ./build
$ ./build/nvibrant 512 512Or build them all for the python utility to use:
# Installs on the user's site-packages (editable)
$ python3 -m pip install -r requirements.txt
$ python3 ./nvibrant/build.pyInputs: Vibrance Levels are numbers from -1024 to 1023 that determines the intensity of the effect. Zero being the "no effect" (default at boot), -1024 grayscale, and 1023 max saturation (200%)
The values are passed as arguments to nvibrant's binary, matching the order of physical ports in your GPU (not the index of the video server). For example, I have two monitors on HDMI and DisplayPort in an RTX 3060 first two outputs, to set vibrance to 512 and 1023, respectively, I would run:
$ nvibrant 512 1023
Display 0:
β’ (0, HDMI) β’ Set Vibrance ( 512) β’ Success
β’ (1, DP ) β’ Set Vibrance ( 1023) β’ Success
β’ (2, DP ) β’ Set Vibrance ( 0) β’ None
...Note: You might need to set nvidia_drm.modeset=1 kernel parameter, but I think it's enabled by default on recent drivers.
If a value is not passed for the Nth physical output, nvibrant will default to zero. When no argument is passed, it will effectively clear the vibrance for all outputs. None means the output is disconnected.
β You might have a display at the later ports, in which case use as:
$ nvibrant 0 0 0 1023
Display 0:
β’ (0, HDMI) β’ Set Vibrance ( 0) β’ None
β’ (1, DP ) β’ Set Vibrance ( 0) β’ None
β’ (2, DP ) β’ Set Vibrance ( 0) β’ None
β’ (3, DP ) β’ Set Vibrance ( 1023) β’ Success
β’ (4, DP ) β’ Set Vibrance ( 0) β’ NoneFor simplicity, a Systemd user service running either uvx for the latest releases (in case of driver updates), or a prebuilt binary directly should cover most users, plus it integrates well with dotfiles repositories!
Create a file at ~/.config/systemd/user/nvibrant.service with the content:
[Unit]
Description=Apply nvibrant
After=graphical.target
[Service]
Type=oneshot
ExecStartPre=/bin/sleep 5
ExecStart=uvx nvibrant 1023 1023
[Install]
WantedBy=default.targetEnable the service with systemctl --user enable --now nvibrant.service
- You can also pin it to a specific version with
uvx nvibrant==1.1.0 (args)to have more control - Or a C++ binary at
~/.local/bin/nvibrantand useExecStart=%h/.local/bin/nvibrant (args) - Sleeping for a few seconds can prevent racing conditions with the display server starting up
Another option is to use uv tools for manual control and/or offline usage:
- Run
uv tool install nvibrantonce (upgrade withuv tool update nvibrant) - Use
ExecStart=uv tool run nvibrant (args)in the service
Important
I have never used a hybrid system, this is unknown and experimental territory.
- Get in touch to improve this section, report good or bad results!
Systems with both integrated and dedicated GPUs (like Intel Iris + NVIDIA) can be tricky, especially laptops.
What often happens is that the iGPU is the primary one that "owns" the displays, with nvidia's frames being passed through it (muxed). This is usually done for power efficiency, only using nvidia on intensive tasks.
-
Desktop users should be fine, as the displays are driven and connected directly on the nvidia card.
-
Your best chances are on disabling the iGPU altogether in the BIOS/UEFI, making nvidia the primary and only GPU in laptops systems - Expect much worse battery life with such!
Linux has a couple solutions for GPU dispatching - PRIME, Optimus and Bumblebee. It is currently unknown if any of these allow both nvibrant to interface with the drivers, and have visual changes altogether.
Note: This is different than libvibrant, which uses the Color Transfer Matrix properties of X11 displays, common to both GPUs.
Some users have reported issues with dithering causing flickering or artifacts on their displays (1) (2), which also lacks a Wayland option to disable in nvidia-settings. Fear not, you can change it with nvibrant too!
- Run:
ATTRIBUTE=dithering uvx nvibrantto disable it on all displays (v1.1+)
Minor quirk, here's the values meanings, default is 2:
enum NvKmsDpyAttributeRequestedDitheringValue {
...AUTO = 0,
...ENABLED = 1,
...DISABLED = 2,
};If you have multiple devices, specify a NVIDIA_GPU=N index:
$ NVIDIA_GPU=1 nvibrant 0 100
Display 0:
β’ (0, HDMI) β’ Set Vibrance ( 0) β’ Success
β’ (1, DP ) β’ Set Vibrance ( 100) β’ SuccessPlease report unknown or unlisted issues to be added here!
-
If you get a "Driver version mismatch" or
ioctlerrors, maybe try rebooting (if you haven't) since the last driver update. Otherwise, you can force the version withNVIDIA_DRIVER_VERSION=x.y.z. It must match what/dev/nvidia-modesetexpects and is currently loaded in the kernel. -
Ensure you have the
nvidia-modesetkernel module loaded, as it is required for the ioctl calls to work. You can check this withlsmod | grep nvidia. Else, add it to your kernel boot parameters. -
It's possible that nvibrant may fail on future or older drivers due to differences between the internal structs and enums in the latest
nvkmsheaders. Please report any issues you encounter!
Integrating this work directly in libvibrant would be the ideal solution, although matching the nvidia driver version could be annoying for a generalized solution. Feel free to base off this code for an upstream solution and PR, in the meantime, here's some local improvements that could be made:
- Make an actual CLI interface with
--help,--version, etc. - I am probably not doing safe-C code or types right
Contributions are welcome if you are more C/C++ savy than me! π