Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Elf

Warms the screen after sunset. One Swift file, menu bar, no lag.

Picking Ember 1900K from the menu bar: blue gain falls to 0.000, red never moves

▶ Full 59-second demo — why 2700K is Night Shift's floor, where the gamma LUT sits in the display pipeline, the presets, auto sunset tracking, and elfctl.

Install

Disk imageElf-1.0.0.dmg (360 KB). Open it, drag Elf to Applications, then run this once:

xattr -dr com.apple.quarantine /Applications/Elf.app

That last step is not optional and it is not a formality. The app is ad-hoc signed, not signed with an Apple Developer ID and not notarized, so Gatekeeper rejects it on sight — spctl -a reports rejected, and without clearing the quarantine flag macOS will refuse to launch it. If you would rather not run that command on a binary from a stranger, build from source below; it takes about four seconds and you get to read all 300 lines first.

Coordinates: the disk image ships with Tel Aviv baked in. Auto mode will be wrong for you unless you build from source with your own LAT / LON. The manual presets work correctly everywhere.

Read this first: you may not need it

Night Shift is built in, and its floor measures at exactly 2700K (full range 2700K–6000K, default 4100K). It runs in Apple's own display pipeline, which is the most robust lane available and immune to the gamma bugs below.

If 2700K is warm enough, use Night Shift and skip this. Settings → Displays → Night Shift, schedule Sunset to Sunrise, slider to More Warm.

Elf exists for one reason: 2700K is Night Shift's hard floor and it cannot be raised. Verified, not assumed — setCCTRange returns false, and setCCT(1900) returns true while readback stays pinned at 2700. Gamma is the only way below 2700K on macOS. That is the whole justification for this program.

How it works

CGSetDisplayTransferByFormula — a hardware LUT in the display scanout stage. Applied once, downstream of compositing. Zero per-frame cost. Public API, not deprecated in the macOS 26 SDK, no entitlement, no TCC prompt, no private framework.

This is the same mechanism f.lux uses — f.lux is not an overlay, contrary to common belief. So Elf is not magically immune to f.lux's problems. What it is: 200 readable lines instead of a binary whose last Mac release was v42.2 in June 2023.

Composes with Night Shift rather than fighting it: they sit at different stages and stack multiplicatively. Run both and you go warmer than either alone.

Build from source

git clone https://github.com/Lowsplix/elf && cd elf
swiftc -O elf.swift -o elf
./elf

Moon icon appears in the menu bar. Run this way it needs no .app bundle, no Info.plist and no Xcode project — setActivationPolicy(.accessory) does at runtime what LSUIElement normally does declaratively. The Swift toolchain comes from Xcode or the Command Line Tools; nothing else to install.

./make-dmg.sh builds the bundle and disk image, if you want your own copy with your own coordinates in it.

Set your coordinates

Auto mode computes the sun locally, so it needs to know where you are. Edit the top of elf.swift before you build — the defaults are Tel Aviv:

let LAT = 32.0853
let LON = 34.7818
let TRANSITION_MINUTES = 60.0   // fade length around sunset/sunrise
let AUTO_NIGHT_KELVIN = 2700.0  // what Auto settles on after dark

Anything within a degree or so is fine; sunset moves by about four minutes per degree of longitude.

Use

  • Auto (sunset) — on by default. Neutral by day, fades to 2700K over the hour after sunset, back at dawn. No location permission, no network.
  • Presets — 6600K neutral down to 1400K "Deep Elf". 6600K is the exact identity point where all three gains land on 1.0, so "Off" is a true no-op.
  • Start at login — writes ~/Library/LaunchAgents/com.adir.elf.plist.
  • Quit — restores normal color immediately.
./elf --selftest   # kelvin curve + solar math

elfctl

Same controls without hunting for the menu bar icon. Lives next to the binary.

./elfctl status            # running? plus the saved prefs
./elfctl start             # load into launchd (writes the plist if absent)
./elfctl stop              # unload; gamma reverts to neutral immediately
./elfctl auto              # back to sunset tracking
./elfctl set ember         # off | daylight | evening | candle | ember | deep
./elfctl set 2200          # or a raw kelvin number
./elfctl presets           # list preset names and their kelvin

A running Elf picks up the change in about a second — elfctl sends SIGUSR1 rather than restarting anything.

Known failure modes in this lane

  • Silent no-op on M5-class Macs running macOS 26.3+. Apple-confirmed and open (FB22273730). The API returns success and readback shows correct values while the screen does not change. Hits f.lux, BetterDisplay, MonitorControl and Lunar identically — an OS bug, not something an app can work around. Trust your eyes, not the return code.
  • Auto Brightness + built-in XDR. Broke gamma in early Tahoe betas; fixed at OS level in dev beta 5, Aug 2025. On builds past that it is not a factor. If tinting ever gets flickery, turning Auto Brightness off is still the first thing to try.
  • Gamma is owned by the setting process and reverts the instant it exits — which is why Elf stays resident, and also why nothing here can permanently damage your display. Kill it and color returns. Elf re-asserts on wake and on display change, both of which silently drop the ramp.
  • Screenshots should be unaffected (the LUT sits after the framebuffer), but this was not verified — verifying it needs Screen Recording permission. Check yourself before sharing screenshots at night.

Verified on macOS 26.2, M1 Max, built-in Liquid Retina XDR.

License

MIT. See LICENSE.

About

Warms the screen after sunset, below Night Shift's 2700K floor. One Swift file, menu bar, no per-frame cost.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages