Skip to content

feat(ui): lite mode for GPU-starved machines, armed by GPU load - #550

Draft
krisf wants to merge 1 commit into
SimoHypers:devfrom
krisf:feat/lite-mode
Draft

krisf wants to merge 1 commit into
SimoHypers:devfrom
krisf:feat/lite-mode

Conversation

@krisf

@krisf krisf commented Oct 25, 2026

Copy link
Copy Markdown

feat(ui): lite mode for GPU-starved machines, armed by GPU load

Closes #549

Why

The app repaints continuously in several places even when nothing is happening: the marquee
loop, the artwork wash and its blur, the artwork-accent crossfade, smooth-scroll lyrics, and
the per-frame progress bar. On a machine with GPU headroom that is invisible. On a machine
where the GPU is busy with something else, it is the difference between a smooth UI and a
stuttery one.

My machine is the extreme version of that: two Radeon R9700s pinned at 100% by local
inference all day, and a 5120×2880@120 display driven by the CPU's 2-CU iGPU. The iGPU has no
bandwidth left to give, so scrolling the library and the queue felt choppy while the desktop
itself was fine. This is the same class of machine as #311 and #302 (UHD 620 / integrated
graphics reports), and the same territory docs/UI-PERFORMANCE.md already works in — those
fixes help every machine, this adds a switch for the ones that need more.

What

Lite mode (Settings → Appearance). When on, the constant repainters go quiet:

  • artwork wash, header blur and the artwork-accent crossfade stop; the artwork-derived
    gradients drop to the flat surfaces that already exist
  • marquee text renders static instead of scrolling
  • lyrics and list scrolling switch to jumps instead of animated tweens
  • the progress bar updates once a second instead of every frame
  • a .lite root class zeroes CSS animations and transitions as a backstop

Nothing is relayouted and no colours are redefined — the same theme, just not animating.

Arm automatically when a GPU is busy (same settings section, Linux only). Turns lite mode
on by itself while a GPU is pegged and back off once it settles, so you don't have to think
about it:

  • one poll per 15 s reads /sys/class/drm/card*/device/gpu_busy_percent (std-only, no new
    dependencies; the pure read takes a base path so the tests feed it a fake sysfs tree)
  • arms after any card holds ≥85% for three samples (~45 s), disarms once every card is under
    40% for eight samples (~2 min), with dwell on both sides so a bursty GPU can't flap it
  • LIMUSIC_FORCE_GPU_BUSY=<0-100> overrides the reading, so the behaviour is testable without
    loading a GPU
  • the switch is hidden on macOS/Windows, where those counters don't exist

Not touched

crates/ (player, innertube, listen-protocol), tauri.conf.json, Cargo.toml/Cargo.lock,
player_configs.json, the updater and .github/ are all at zero diff — this is additive UI
state plus one read-only Linux command. Playback and audio quality are completely
untouched
; the mode changes what is painted, never what is decoded. Lite mode is off by
default and the normal rendering path is byte-for-byte unchanged when it is off.

What I tested

  • cargo fmt --all --check clean
  • cargo test --all green — including three new unit tests: parses real readings and padded
    zeros, skips connector dirs and non-numeric counters, [] on an unreadable base instead of
    panicking, and the force-seam override
  • cargo clippy --all-targets: no new findings (the existing ones in lyrics.rs, state.rs,
    discord.rs, crates/ are pre-existing and untouched)
  • pnpm check: 0 errors, 0 warnings · pnpm test: 22/22 self-checks · pnpm build: clean
  • Real-machine check on the machine described above: with lite mode on, the choppy scroll in
    the library and queue goes away; playback, gapless transitions and lyrics sync unchanged.
    The auto-arm seam was verified with LIMUSIC_FORCE_GPU_BUSY=95 (arms within ~45 s, wash
    flattens without any saved setting changing) and =0 (disarms within ~2 min).

Generated with AI

Per CONTRIBUTING: this code was written by a locally-hosted Qwen3.8-27B model, driven from
a written plan by its operator (me), with every gate re-run independently by me after the run
and the deviations recorded. I've read the diff and take responsibility for it.

Opened as a draft pending agreement on scope in #549 (their CONTRIBUTING asks for that first).

@coderabbitai

coderabbitai Bot commented Oct 25, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Adds an opt-in Lite mode (Settings > Appearance) that stops the app's
continuous repaints: artwork washes and blur, artwork-accent crossfade,
marquee and smooth-scroll animations, and per-frame progress updates all
go quiet, so a machine whose GPU is busy elsewhere can hold a smooth
frame. A companion Linux-only switch arms it automatically while any GPU
is pegged (15 s /sys gpu_busy_percent poll, 85%/45 s arm, 40%/2 min
disarm, runtime-only state); LIMUSIC_FORCE_GPU_BUSY overrides the
readings for testing. Playback and audio are untouched.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant