Skip to content
xvin84Public

About

Screen-edge ambilight for LED strips over the Adalight serial protocol: Windows (DXGI/dxcam) and Linux/Wayland capture, PySide6 GUI with live preview and tray

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Repository files navigation

Adalight

English | Русский

⚠️ Beta. Adalight is pre-1.0 — a few rough edges are expected. Please report bugs and ideas in the issues.

Screen-edge ambient lighting (ambilight) for an LED strip behind your monitor: the app captures the screen, averages the colors along the edges and streams them to an Arduino/ESP over the classic Adalight serial protocol.

Runs on Windows (DXGI Desktop Duplication via dxcam, mss fallback) and Linux/Wayland (Hyprland/wlroots: wf-recorder, grim fallback), with a Qt (PySide6) GUI: every setting in one window, a live preview of the LED layout, and a tray icon so the lighting keeps running with the window closed.

Adalight main window

Plugin manager: installed plugins and catalog

Features

  • Boards introduce themselves: the bundled firmware answers with its id, firmware version, LED count and baud rate, so the app picks the baud rate on its own, lists the board as "Backlight (A1B2C3D4)" instead of "/dev/ttyUSB1", and remembers settings per board rather than per port number. Boards with older firmware keep working in compatibility mode.
  • "Blink" and board self-test: not sure which COM port is yours? Make the strip breathe. Want to take the computer out of the chain? Let the board run a chase dot by itself.
  • Smart port picker: unidentified ports go into their own "other ports" group and are never hidden; recognition is by USB descriptor (VID/PID), like Arduino IDE's "Get Board Info", plus a "Show all ports" checkbox.
  • Channel order (RGB/GRB/BGR/…) — WS2812 strips usually expect GRB. The baud rate moved to "advanced": normally nobody needs to see it.
  • LED counts per side, start corner, strip direction (cw/ccw), X/Y mirroring.
  • Monitor selection, target FPS, gamma / brightness / saturation / smoothing.
  • Live settings: image parameters, schedule and adaptive brightness apply instantly without restarting (and without resetting the board); layout/port changes auto-apply 5 seconds after the last edit.
  • Brightness schedule: time ranges with their own brightness (e.g. 08:00–20:00 → 0.9, 20:00–00:00 → 0.5), overnight ranges supported, the default brightness applies outside all ranges.
  • Adaptive brightness: dims the strip on dark scenes and ramps it up on bright ones, with min/max bounds and reaction speed.
  • Autostart: launch on login minimized to tray with lighting on (Windows registry / XDG autostart).
  • Lamp mode: solid color / gradient / rainbows / breathing / fireplace (a hearth with sparks) / comet / aurora / starry sky — the strip works without screen capture.
  • Tray notifications: lighting on/off (when the window is hidden) and new version releases (checked every 30 minutes); can be disabled in System.
  • Music mode: system loopback audio drives the LEDs — a perimeter spectrum, a bass-driven pulse, bass waves and beat flashes, with adjustable sensitivity.
  • Night mode: one button makes everything warmer (3400K), dimmer (×0.6) and smoother — on top of your current settings.
  • Color pipeline: color temperature (white balance) and a shadow noise cut-off so dark scenes don't make the LEDs glow with noise.
  • Auto-update: the app checks GitHub Releases, downloads the new binary and restarts itself — no manual downloading; with "update automatically" enabled new versions install silently at startup. After an update a "What's new" dialog shows the changelog (across several versions too), in the interface language with a language switch right in the window.
  • Plugin manager: a dedicated window with two views — Installed (enable/disable, per-plugin settings, delete) and Catalog (search and one-click install official/community plugins). Plugins are .py files with create_plugin() in <config>/plugins/; a plugin can declare a settings_schema and the manager builds its settings form automatically — no GUI code needed. Docs and a template: docs/PLUGINS.md (ru), examples/plugins/break_reminder.py.
  • Notification flashes (built-in plugin): Telegram — a blue flash, Discord — purple; the "any app" mode colors the flash from the sending app's icon. The flash is a "ripple" — a drop with a wave spreading along the strip (a plain blob is also available). Position is set by dragging a spot along the screen edge, works over any mode. Windows only sees system notifications, so enable system notifications in the sending app (Telegram: Settings → Notifications → use Windows notifications).
  • Power guard (built-in plugin, off by default): estimates the strip current from every frame — a WS2812B channel draws 20 mA at full, so a white LED is 60 mA — and dims the whole strip while it is over the power budget (500 mA for USB by default, minus a safety headroom and the board's own draw), never below the minimum brightness you set. Dropping is instant, recovery is smooth, and the status card shows the estimate live. Datasheet currents are hidden behind "Show advanced settings". It is a calculation, not a measurement — it lowers the risk on bright scenes but does not replace an external power supply.
  • Report a bug / idea: buttons in System open a prefilled GitHub issue with diagnostics (version, OS, capture backend, LED count) attached.
  • WLED transport (beta): an ESP strip running WLED over Wi-Fi (UDP DRGB/DNRGB, port 21324) — no wire, no baud-rate cap.
  • Modern UI: sidebar navigation with SVG icons, a status card (state · backend · fps), live preview with the captured screen and sampling zones — clicking an LED in the preview flashes it on the real strip; dark / light / system theme.
  • Language: Russian and English UI (System → Language); additional languages install as locale plugins (a create_locale() file), template in examples/locales/en.py.
  • First-run wizard: port → LEDs → side check in three steps (also available anytime: System → "Setup wizard…").
  • Windows installer (Adalight-Setup.exe): installs per-user (no admin), start-menu/desktop shortcuts and optional autostart.
  • Single-instance: launching the app again just raises the running window.
  • Settings profiles: built-in presets 🎬 Movie / 🎮 Game / 💼 Work (layered on top of your hardware settings) plus your own saved profiles; one-click switching from the window or the tray menu.
  • White balance: per-channel R/G/B multipliers to calibrate the strip.
  • Import/export settings to a JSON file — backup and transfer between machines.
  • Calibration test modes: color-per-side fill and a running-dot chase.
  • Headless CLI for autostart setups; GUI and CLI share the same JSON config (%APPDATA%\adalight\config.json on Windows, ~/.config/adalight/ on Linux).

If FPS is low

  • Baud rate is a hard cap: at 115200 the wire fits ~11.5 KB/s, i.e. ~76 fps for 48 LEDs but only ~13 fps for 300. The app detects whatever the firmware is flashed with, so raising it means reflashing SERIAL_RATE (ESP boards handle 921600, classic Arduino 500000).
  • The status bar shows which capture backend is actually running; on Windows the fast path is bettercam (a maintained dxcam fork), then dxcam, and if both fall back to mss the reason is shown. The mss backend captures only the edge bands rather than the full screen.

Download

Grab a binary from the latest release — no Python required:

  • Windows: Adalight-Setup.exe (installer, recommended) or portable Adalight.exe
  • Linux: Adalight-linux-x86_64 (then chmod +x Adalight-linux-x86_64; Wayland capture additionally needs wf-recorder installed)

Port permissions (Linux)

If you see "no access to the port" on start — install the bundled udev rule: the app offers to install it with one button, or copy packaging/99-adalight.rules to /etc/udev/rules.d/ manually and run:

sudo udevadm control --reload-rules && sudo udevadm trigger

The rule grants access to the user of the graphical session via systemd-logind — no group membership or re-login needed (replug the board over USB after installing). Fallback for systems without logind: sudo usermod -aG dialout $USER (uucp group on Arch), then re-login.

Firmware

Your board (Arduino/ESP) needs an Adalight-compatible sketch. A ready-to-flash reference sketch is in firmware/ — original author AlexGyver (https://alexgyver.ru/arduino_ambilight/), included with attribution and only lightly adapted. See firmware/README.md for flashing and how the sketch settings map to the app.

Run from source

Requires uv:

uv sync

# GUI
uv run main.py

# headless & service modes
uv run main.py --live
uv run main.py --sides          # test: top=red, right=green, bottom=blue, left=yellow
uv run main.py --chase          # test: running dot
uv run main.py --off            # turn the strip off
uv run main.py --list-monitors
uv run main.py --list-ports
uv run main.py --identify      # which port is the board, what firmware, what baud
uv run main.py --self-test     # the board runs the chase dot on its own

Calibration

  1. Start “Test: sides”: the top edge must light up red, right green, bottom blue, left yellow. If sides are mixed up, adjust the start corner, direction or mirroring — the preview in the window mirrors your changes live.
  2. If the hues are wrong (red shows as green etc.), change the channel order — WS2812 is usually GRB.
  3. “Test: chase” runs a single bright dot along the strip to verify the exact LED order; the first LED is marked with a ring in the preview.
  4. Not sure which port is your board? “Blink” on the Device tab makes the strip breathe. To check the strip and board with the computer taken out of the chain, use “Board self-test” — the firmware drives the dot itself.

Tech map

Layer Module What it does
Capture capture/ Windows: bettercam → dxcam (DXGI, grab() polling), mss fallback; Wayland: wf-recorder / grim; mss grabs edge bands only
Geometry geometry.py LED layout around the perimeter, color-sampling zones
Engine engine.py Capture→process→send loop (Qt-free); live/lamp/music/test modes; schedule, adaptive and night brightness; live settings
Effects effects.py, audio.py Lamp (solid/gradient/rainbows/breathing), music (FFT + AGC over loopback audio via soundcard)
Device device.py Adalight protocol, channel order, LUT gamma, color temperature, shadow cut-off
Board probe probe.py Identity line, baud autodetect, breathing blink, board self-test
Frame filters pipeline.py Registry of per-frame hooks before sending — what Power guard runs on
GUI gui/ PySide6: tabs, live preview with zones, tray, themes, auto-update
Infra updates.py, autostart.py, CI GitHub Releases (auto-update), autostart (registry/XDG), exe+installer+linux binary built on v* tags

Roadmap

  • Settings profiles ("Movie", "Game", "Work") with quick switching from the tray
  • WLED-UDP transport — ESP strip over Wi-Fi, no wire and no baud-rate cap (beta)
  • Notification integrations — a color flash: Telegram blue, Discord purple
  • Plugin system — custom effects and integrations without rebuilding (first API)
  • More music effects (bass waves, beat flashes)
  • Multi-monitor — independent strip segments across screens
  • xdg-desktop-portal / PipeWire capture — GNOME and KDE support on Wayland
  • UI localization — Russian and English; new languages ship as locale plugins
  • macOS build (a tester with a Mac is welcome)

Got an idea? Open an issue.

How it works

  • Header "Ada" + count_hi + count_lo + (hi^lo^0x55), then 3 bytes per LED — the standard Adalight protocol, compatible with common Arduino sketches.
  • Edge zones are precomputed per LED from the layout; each frame is averaged per zone, exponentially smoothed and gamma-corrected through a precomputed LUT (no per-frame pow).
  • The capture/processing loop is Qt-free (adalight/engine.py); the GUI runs it in a background thread, the CLI drives it directly.
adalight/
  config.py        # settings dataclass + JSON load/save
  geometry.py      # LED layout and capture zones
  device.py        # Adalight protocol, LUT gamma, channel order
  engine.py        # capture -> process -> send loop (no Qt)
  pipeline.py      # frame filters applied right before sending
  capture/         # backends: dxcam (Windows), mss, wf-recorder, grim
  cli.py           # headless modes
  gui/             # PySide6: main window, preview, tray
main.py            # entry point: GUI without args, CLI otherwise

Releases

  • CI (.github/workflows/ci.yml): ruff + pytest on every push/PR.
  • Release (.github/workflows/release.yml): pushing a v* tag builds Adalight.exe with PyInstaller on a Windows runner and publishes a GitHub Release. The tag must match the version in pyproject.toml.

License

MIT

About

Screen-edge ambilight for LED strips over the Adalight serial protocol: Windows (DXGI/dxcam) and Linux/Wayland capture, PySide6 GUI with live preview and tray

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages