Melanopic-aware color temperature daemon for KDE Plasma Wayland.
Circadia replaces KDE's built-in Night Color with a multi-phase solar elevation profile inspired by f.lux. Instead of a single day/night transition, it continuously interpolates color temperature across five twilight phases — producing gradual shifts that track the sun's actual position. At high latitudes where twilight lasts hours, this makes a real difference.
Blue light suppresses melatonin production via melanopsin-containing retinal ganglion cells (ipRGCs). These cells are most sensitive around 480nm — right in the peak emission of typical displays at 6500K. Research shows that reducing display color temperature to 1900–2700K at night cuts melanopic irradiance by 5–10x, meaningfully reducing circadian disruption.
KDE Night Color does a basic warm/cool toggle. f.lux gets this right with multi-phase solar profiles, but it's closed-source and X11-only on Linux. Circadia fills the gap: open-source, Wayland-native, research-informed defaults.
git clone https://github.com/guitaripod/circadia
cd circadia
makepkg -si
systemctl --user enable --now circadiaThat's it. Circadia auto-detects your location from KDE settings, generates a config, and starts managing your display temperature.
What happens on install
makepkgbuilds a native Arch package with a 1.4MB stripped binarypacmaninstalls the binary, systemd service, and example config- The systemd user service starts circadia in the background
- On first run, circadia reads your coordinates from
~/.config/knighttimerc(KDE's location config) and writes~/.config/circadia/config.toml - It switches KDE Night Color to constant mode and begins controlling the temperature
- On shutdown (or
systemctl stop), it restores your original Night Color settings
circadia # start daemon (foreground)
circadia status # show current state
circadia preview 2700 # override temperature
circadia reset # clear override
Example status output
elevation: -8.5°
phase: Nautical twilight
temperature: 3106K
updated: 2026-03-09T19:09:35+02:00
Temperature is a continuous function of solar elevation, linearly interpolated between these waypoints:
| Elevation | Temperature | Phase |
|---|---|---|
| ≥ +6° | 6500K | Daylight |
| 0° | 4500K | Golden hour |
| -6° | 3400K | Civil twilight |
| -12° | 2700K | Nautical twilight |
| ≤ -18° | 1900K | Night |
At 60°N latitude in March, the sun takes over two hours to travel from 0° to -18° — so the transition from 4500K to 1900K happens gradually over that entire period. No abrupt shifts.
Why these specific temperatures
The waypoints target melanopic response, not just visual comfort:
- 6500K — neutral daylight, full alertness, no filtering needed
- 4500K — mild warm shift as the sun sets, roughly matching golden hour ambient light
- 3400K — equivalent to warm white LED lighting, typical evening indoor light
- 2700K — incandescent-equivalent, significant melanopic reduction (~70% less than 6500K)
- 1900K — candlelight-equivalent, near-zero melanopic stimulation
The exact melanopic impact depends on your display's spectral power distribution (SPD), which varies by panel technology. These defaults are conservative and work well across most displays. Tune the waypoints in config if you have specific SPD data for your monitor.
Auto-created at ~/.config/circadia/config.toml on first run.
[location]
latitude = 60.16952
longitude = 24.93545
elevation_meters = 0.0
[[profile.waypoints]]
elevation = -18.0
temperature = 1900
[[profile.waypoints]]
elevation = -12.0
temperature = 2700
[[profile.waypoints]]
elevation = -6.0
temperature = 3400
[[profile.waypoints]]
elevation = 0.0
temperature = 4500
[[profile.waypoints]]
elevation = 6.0
temperature = 6500
[daemon]
update_interval_secs = 10Configuration reference
[location]
latitude/longitude— decimal degrees. Auto-detected from KDE on first run.elevation_meters— altitude above sea level. Affects sunrise/sunset times slightly.
[[profile.waypoints]]
elevation— solar elevation angle in degrees. Negative = below horizon.temperature— color temperature in Kelvin (1000–6500). Must be sorted by ascending elevation, minimum 2 waypoints.
[daemon]
update_interval_secs— how often to recalculate and apply temperature. Default 10s.
Architecture
Circadia is a ~550-line Rust daemon with no async runtime and no D-Bus library.
- On startup, saves existing KDE Night Color settings and switches to constant mode
- Every 10 seconds: calculates solar elevation using the NREL SPA algorithm (±0.0003° accuracy), interpolates temperature from the waypoint profile, and writes the result to
kwinrcviakwriteconfig6 --notify - KWin's
KConfigWatcherdetects the change and applies the new temperature natively through its color management pipeline — smooth built-in transitions, no OSD popups - On SIGTERM/SIGINT, restores original Night Color settings and cleans up
The preview command writes an override file to $XDG_RUNTIME_DIR/circadia.override. The daemon checks this file each cycle and uses the override value instead of the calculated temperature. reset deletes the file.
Why not use KWin's preview() API
KWin's NightLight D-Bus interface exposes a preview(temperature) method. It works, but it shows an OSD popup ("Color Temperature Preview") on every call — hardcoded in KWin's source with no way to suppress it. Calling it every 10 seconds is unusable.
Instead, circadia writes directly to kwinrc and uses --notify to trigger KWin's config watcher, which applies the temperature natively without any popup.
systemctl --user disable --now circadia
sudo pacman -R circadiaNight Color settings are restored to their original state when the daemon stops.
- KDE Plasma 6 on Wayland
kwriteconfig6/kreadconfig6(provided bykconfig, a core KDE dependency)