# wluma: brightness control that learns your habits on Wayland

> wluma is a Rust daemon for Wayland compositors that adjusts screen brightness from the content on screen and the ambient light around you, after learning your manual brightness habits. It grabs frames through DMA-BUF and analyses them on the GPU with Vulkan, and it falls back to ambient light alone when no capture protocol works.

**max-baz/wluma** — Automatic brightness adjustment based on screen contents and ALS

- Repository: https://github.com/max-baz/wluma
- Stars: 952 · Forks: 49
- Language: Rust
- License: ISC
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/max-baz-wluma

## wluma does nothing on the first launch, on purpose

The most important line in the README for anyone installing this is the one that says the app will not do anything on the first launch.

The reasoning is in the next sentence. You have to adjust the brightness by hand a few times, in different environments and with different screen contents, and only then does wluma learn your preferences and begin adjusting the screen by itself. The tool is not applying a fixed brightness curve that it guesses at; it is modelling the relationship between what you see, how much light is around you, and the brightness you chose to set.

That design explains the rest of the behaviour. The stated idea is that the screen brightens when you look at a dark window, a fullscreen terminal for example, and darkens when you look at a bright window such as a web browser. The algorithm also takes the amount of ambient light into account, so the same window can be brighter during the day than during the night. In other words, the target is a preference model, not a brightness sensor response.

Operationally this means the first few minutes after installing will look like a broken feature, and it is not. Brightness can also be changed, status checked and the running daemon controlled through CLI commands, with `wluma help` listing the available options.

## Capture protocol selection happens once, at startup

For a compositor to react to what is on screen, it has to see the screen, and on Wayland that is a negotiated protocol rather than a simple read of the framebuffer. wluma handles this with a probe at startup.

With the default `capturer = "auto"`, wluma tries the supported Wayland protocols followed by PipeWire sources once during startup. If none works, it uses ambient light only for the rest of the process lifetime. Once a protocol produces usable frames, wluma keeps using that protocol and reconnects only that protocol after interruptions. That decision is sticky by design, which avoids a situation where the daemon silently switches capture method mid-session and changes how the analysis behaves.

The supported list is specific. `ext-image-copy-capture-v1` is described as the newest protocol that potentially is or will be supported by any modern Wayland desktop environment, and it also requires `ext-image-capture-source-v1` and `linux-dmabuf-v1`. `wlr-screencopy-unstable-v1` is supported by any wlroots-based compositor such as sway, and by Hyprland, and it also requires `linux-dmabuf-v1`. `wlr-export-dmabuf-unstable-v1` covers the remaining wlroots-based compositors. PipeWire streams are supported as well, coming from KWin's private screencast protocol, Mutter's private ScreenCast API, or the generic ScreenCast portal.

The practical consequence is that compositor support is what decides the experience. On a compositor with none of these, wluma still runs, but it is an ambient light dimmer and nothing more.

## Four ways to read ambient light, and one that can abort the daemon

The ambient light sensor is the second input, and the selection logic is more layered than most users expect.

When ALS configuration is omitted, wluma uses an external ALS whenever `$XDG_RUNTIME_DIR/wluma/als.sock` is a Unix socket, otherwise it uses an available IIO ambient light sensor, then the Apple SMC sensor of an Intel MacBook, or continues without one. The selection is updated while wluma runs, as sources appear and disappear. For IIO it first tries `iio-sensor-proxy` over the system D-Bus and then direct IIO discovery under `/sys/bus/iio/devices`. One failure mode is worth knowing: if an IIO sensor is detected but neither method can read it, wluma exits with an error rather than treating the sensor as absent.

Explicit sections override the automatic choice, and the README gives the paths directly. For a direct sysfs poll:

```toml
[als.iio]
path = "/sys/bus/iio/devices"
```

For an Intel MacBook, where the sensor is exposed through the applesmc platform driver as `/sys/devices/platform/applesmc.<id>/light`, wluma uses the brighter reading on the lux scale, and the path is optional when the first matching device is fine:

```toml
[als.applesmc]
path = "/sys/devices/platform/applesmc.768/light"
```

For an external daemon, the contract is a Unix stream socket server sending one non-negative decimal value per line, at least once every two seconds even when the value is unchanged, and the scale can be set to linear:

```toml
[als.external]
path = "/run/user/1000/home-assistant/als.sock"
scale = "linear"
```

The `lux` scale accepts illuminance values without an upper bound. The `[als.webcam]`, `[als.time]` and `[als.none]` sections exist as further overrides. The configuration file itself is optional, at `$XDG_CONFIG_HOME/wluma/config.toml`, since everything is auto-detected to the best extent possible.

## Permissions decide how smooth the brightness transition feels

Accessing a backlight device is privileged, and the README gives three ways to arrange it, with a clear recommendation ordering.

The first is direct driver access: install the supplied `90-wluma-backlight.rules` udev rule, add your user to the `video` group and reboot. The README marks this as the fastest option and the one most likely to be a requirement for smooth transitions, and the rule file sits at the top level of the repository, ready to be placed in `/etc/udev/rules.d/`. The second is to run on a system that uses `elogind` or `systemd-logind`, since they provide a safe interface for unprivileged users to control the device brightness through `dbus` with no configuration necessary. The third is to run as `root`, which the README explicitly does not recommend.

That ordering is a performance decision dressed as a permissions decision. A udev rule and group membership give the daemon direct access to the backlight device, so a brightness change is a write to the device rather than a request routed through a system service, and the README ties that directly to smooth transitions. The logind route keeps the privilege separation intact at the cost of an extra hop.

If you build the app yourself, the systemd unit `wluma.service` can be placed in `~/.config/systemd/user/` to run the daemon as a user service. The `wluma.service` file and `90-wluma-backlight.rules` are both in the repository root, so a source install gives you everything needed for either arrangement.

## Building from source and running the binary the first time

Packages exist for several distributions, including Alpine Linux, Arch Linux with a wluma and a wluma-git package, NixOS and Fedora Linux through the Terra repository, and a repology badge tracks the packaging status across repositories.

If you would rather build it, the README asks for the latest stable Rust, since older toolchains can produce compilation errors, and suggests rustup as the easiest route. On Ubuntu the dependency list is given explicitly:

```bash
sudo apt-get -y install v4l-utils libv4l-dev libudev-dev libvulkan-dev libdbus-1-dev libpipewire-0.3-dev
```

Then the build itself is a single command, and the binary lands in the release directory:

```bash
cargo build --locked --release
```

The repository also ships a justfile with the usual set of tasks. Linting runs formatting in check mode and clippy with warnings denied, tests run through cargo, and the release task builds a tarball with a detached signature and also a vendored-sources variant that carries its own cargo configuration for offline builds. The release task also removes the plain tarball from the distribution directory.

For a first run, start the binary and then adjust the brightness by hand several times in different conditions, because until the tool has learned your preferences it will not change anything on its own. Use `wluma help` to see the CLI options for changing brightness, checking status and controlling the daemon.

## Why the GPU is in the frame analysis path

The performance claim in the README is specific about its mechanism. The app is described as having minimal impact on system resources and battery life even though it can monitor screen contents several times a second, and the stated reason is that it imports DMA-BUF screen buffers and does the computations on the GPU using the Vulkan API.

The dependency list backs that up. The crate `ash` is the Vulkan binding for Rust, pinned at about 0.38 with the `linked` feature and default features off, and `drm-fourcc` is present for working with DRM pixel formats. Wayland support comes through `wayland-client`, `wayland-backend` with `dlopen`, and the `wayland-protocols`, `wayland-protocols-wlr` and `wayland-protocols-plasma` crates, the last two with the `client` feature, and the protocols crate is enabled with `client`, `staging` and `unstable` features so that the newer capture protocols and the compositor-specific ones are reachable. `pipewire` at about 0.10 with the `v0_3_33` feature covers the PipeWire capture path.

Two other dependencies hint at the wider device surface. `v4l` at about 0.14 with `libv4l` is the Video4Linux binding, and `ddc-hi` is a DDC/CI implementation, which is the monitor-side channel for brightness on displays that expose it. `dbus` is there for the logind and iio-sensor-proxy paths, and `inotify` and `xdg` cover file watching and the XDG base directories.

## Where wluma is the wrong tool

There are clear boundaries, and the README states most of them.

The first is X11. This is a tool for Wayland compositors, and the capture protocol list contains no X11 path, so on an X11 session there is neither a frame source nor a reason to expect the daemon to adjust anything.

The second is a compositor with no supported capture protocol and no ambient light sensor. In that case the README is explicit: wluma uses ambient light only, and if there is no ambient light source either it continues without one, which means no adjustment at all. The set of compositors in practice is wlroots-based ones such as sway, Hyprland, and the PipeWire capable desktops behind the ScreenCast portal.

The third is the learning period. The tool does nothing on the first launch, and a user who installs it and immediately concludes that it is broken will be right about the observation and wrong about the cause. A device used only at one desk under one lighting condition will also learn a narrow model, which is a behaviour of the design rather than a defect.

The fourth is privacy and surface area. The tool captures your screen and reads a light sensor, and it can talk to a PipeWire portal, an I2C ambient light sensor, a system service over D-Bus, or a local socket. On a shared or managed machine, the udev rule, the `video` group membership and the optional external ALS are the pieces a security review has to look at.

On licensing, the project is under the ISC licence, which is permissive and short, and the Cargo.toml records the same licence and names Maxim Baz and Cyril Levis as authors. Maintenance is brisk: the last push was on 2026-10-18, and releases 5.0.4, 5.0.3 and 5.0.2 landed on 2026-10-03, 2026-09-29 and 2026-09-22.

## Conclusion

wluma fits a Wayland user on sway, Hyprland or a similar compositor who wants brightness to follow the window in front of them rather than the clock, and it fits laptops with an ambient light sensor that can be reached through iio-sensor-proxy, Apple SMC or an external daemon. It does not fit an X11 session, a setup with no supported capture protocol and no ambient light sensor, or a user who expects the screen to respond before they have taught it their preferences. Before adopting it, run the binary once and adjust the brightness by hand a few times as the README asks, then check that the chosen ALS section in $XDG_CONFIG_HOME/wluma/config.toml resolves, and decide whether to install 90-wluma-backlight.rules for direct driver access.

## FAQ

### Why does wluma not change the brightness on the first run?

The README states that wluma will not do anything on the first launch. You have to adjust the brightness by hand a few times, in different environments or with different screen contents, so that the app learns your preferences before it starts adjusting the screen automatically.

### Which screen capture protocols does wluma support?

The supported list is ext-image-copy-capture-v1, which also needs ext-image-capture-source-v1 and linux-dmabuf-v1, wlr-screencopy-unstable-v1, which is used by wlroots-based compositors such as sway and by Hyprland, and wlr-export-dmabuf-unstable-v1. PipeWire streams are also supported, coming from KWin's private screencast protocol, Mutter's private ScreenCast API or the generic ScreenCast portal.

### How do I give wluma permission to change the backlight?

The README lists three options: install the supplied 90-wluma-backlight.rules udev rule, add your user to the video group and reboot for direct driver access, which is described as the fastest option and most likely required for smooth transitions, or run on a system with elogind or systemd-logind, which offer an unprivileged D-Bus interface, or run as root, which is not recommended.

### Can wluma use an ambient light sensor from another program?

Yes. An external ALS is a Unix stream socket server that sends one non-negative decimal value per line, at least once every two seconds, and wluma uses it whenever $XDG_RUNTIME_DIR/wluma/als.sock is a Unix socket. The external section also accepts a path and a scale, and the configuration file is $XDG_CONFIG_HOME/wluma/config.toml.

## Sources

- [Issues](https://github.com/max-baz/wluma/issues)
- [License: ISC](https://github.com/max-baz/wluma/blob/main/LICENSE)
- [max-baz/wluma on GitHub](https://github.com/max-baz/wluma)
- [README](https://github.com/max-baz/wluma/blob/main/README.md)
- [Releases](https://github.com/max-baz/wluma/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/max-baz-wluma
