Investigated 2026-08-09 by a Fable agent against Hyprland 0.56.1 live options, current wiki source, and this machine's client state. Firefox look itself (moz-mac, user.js, vertical tabs) landed in PR #20; this issue is the compositor side.
Problem
The original look — opacity 0.90 0.93 on firefox + global blur (size 3, passes 2) — was reverted on 2026-08-01: video playing in a translucent+blurred Firefox measured 96% iGPU / 24% dGPU vs 47% / 0% opaque. Root cause: video damages a large region every frame, and a translucent+blurred window forces Hyprland to re-blur everything behind it per frame. Current state (in repo): windowrule = opacity 1.0 1.0 + no_blur on for firefox/discord/brave (hyprland.conf ~117-130, with the measurement comment block).
Verified facts (0.56.1)
decoration:blur:new_optimizations defaults ON; ignore_opacity defaults ON; xray exists, off.
- Window rules support per-window
xray on, no_blur, and 3-value opacity "active inactive fullscreen".
hyprctl clients -j exposes per-window inhibitingIdle — Firefox sets an idle inhibitor during media playback → scriptable 'video playing' signal.
- amdgpu load readable at
/sys/class/drm/card0/device/gpu_busy_percent (card0=amdgpu, card1=nvidia).
Ranked workarounds
- Per-window xray (primary):
windowrule = xray on, match:class firefox + restore opacity 0.90 0.93. Blur then samples only layers below tiled windows (effectively wallpaper) — a static, cacheable backdrop, so video self-damage stops forcing re-blurs. Identical look for tiled Firefox; only divergence: a FLOATING Firefox shows blurred wallpaper instead of the windows under it. Expected near-opaque cost; the wiki only documents the floating case, so the tiled-cache benefit is the hypothesis the measurement phase tests.
- Fullscreen-opaque third value (free, compose with 1):
opacity 0.90 0.93 1.0 — fullscreen video (worst case) renders opaque; one line, zero cost.
- inhibitingIdle watcher (fallback): tiny user-service polling
hyprctl clients -j (~1 s) or socket2-event-driven; on a firefox client flipping inhibitingIdle: true, tag it and apply opacity 1.0 override via a tag rule; restore on false. Exact look idle, exact opaque perf during ANY playback (windowed included). Costs a moving part; ~1 s translucent at video start; muted autoplay may not inhibit idle (untested).
- Chrome-only translucency via userChrome (last resort): translucent toolbar/tabs/sidebar, opaque content. Savings not guaranteed (blur pass is per-window) and fragile across Firefox releases.
Rejected: smaller blur size/passes (look degrades, cost still damage-scaled); per-window blur size (doesn't exist on 0.56); global xray (alters every floating window; per-window rule strictly better).
Plan
- Phase 1: firefox only — delete the firefox
opacity 1.0 1.0 and no_blur lines, add windowrule = xray on, match:class firefox + windowrule = opacity 0.90 0.93 1.0, match:class firefox. Discord/brave stay opaque.
- Phase 2: measure (protocol below). Accept if windowed-video iGPU ≤ ~55% (opaque baseline 47%) and dGPU hardware decode stays engaged. Reject if >70%.
- Phase 3 (only on reject): implement the inhibitingIdle watcher, keep xray.
Traps
xray requires new_optimizations (default on) — do not disable it while 'optimizing'.
ignore_opacity defaults ON — if blur looks missing after restoring opacity, check this before stacking hacks.
- Rule order: last match wins. DELETE the old firefox
opacity 1.0 1.0 / no_blur lines rather than appending after them — appending before changes nothing.
- Three windowrule syntaxes exist in the wild (classic conf = this repo, lua = current wiki, windowrulev2 = old forums). Use the repo's
match:class style; run hyprctl configerrors after every edit.
- hyprland.conf is stowed: change
home/.config/hypr/hyprland.conf in the repo AND sync live (on the laptop they are separate real files).
- Rules belong in the main conf's OVERRIDES area — never in
~/.local/share/ambxst/ (generated, rewritten by axctl).
- Update the 2026-08-01 comment block (lines ~117-130) to reflect the new approach instead of contradicting it.
- The
ffwarm special-workspace pre-warmed Firefox window inherits the same class rules; remember it when counting firefox clients in the phase-3 watcher.
- Phase 3 only: verify
set_prop/tagwindow syntax on 0.56 via hyprctl help first; wiki examples are lua-flavored.
Verification protocol
Baseline on the current opaque config first, then after each phase, same monitor layout:
- Same 1080p60 YouTube video, windowed, on the AMD-driven monitor, 120 s.
- iGPU:
for i in $(seq 60); do cat /sys/class/drm/card0/device/gpu_busy_percent; sleep 2; done | awk '{s+=$1} END {print s/NR"%"}'
- dGPU:
nvidia-smi dmon -s u -c 60 (sm + dec columns).
uptime load before/after; confirm hyprctl clients -j | jq '.[] | select(.class=="firefox") | .inhibitingIdle' flips true during playback (validates the phase-3 signal too).
- Repeat fullscreen — must match the opaque baseline exactly (validates the third opacity value).
Investigated 2026-08-09 by a Fable agent against Hyprland 0.56.1 live options, current wiki source, and this machine's client state. Firefox look itself (moz-mac, user.js, vertical tabs) landed in PR #20; this issue is the compositor side.
Problem
The original look —
opacity 0.90 0.93on firefox + global blur (size 3, passes 2) — was reverted on 2026-08-01: video playing in a translucent+blurred Firefox measured 96% iGPU / 24% dGPU vs 47% / 0% opaque. Root cause: video damages a large region every frame, and a translucent+blurred window forces Hyprland to re-blur everything behind it per frame. Current state (in repo):windowrule = opacity 1.0 1.0+no_blur onfor firefox/discord/brave (hyprland.conf ~117-130, with the measurement comment block).Verified facts (0.56.1)
decoration:blur:new_optimizationsdefaults ON;ignore_opacitydefaults ON;xrayexists, off.xray on,no_blur, and 3-value opacity"active inactive fullscreen".hyprctl clients -jexposes per-windowinhibitingIdle— Firefox sets an idle inhibitor during media playback → scriptable 'video playing' signal./sys/class/drm/card0/device/gpu_busy_percent(card0=amdgpu, card1=nvidia).Ranked workarounds
windowrule = xray on, match:class firefox+ restoreopacity 0.90 0.93. Blur then samples only layers below tiled windows (effectively wallpaper) — a static, cacheable backdrop, so video self-damage stops forcing re-blurs. Identical look for tiled Firefox; only divergence: a FLOATING Firefox shows blurred wallpaper instead of the windows under it. Expected near-opaque cost; the wiki only documents the floating case, so the tiled-cache benefit is the hypothesis the measurement phase tests.opacity 0.90 0.93 1.0— fullscreen video (worst case) renders opaque; one line, zero cost.hyprctl clients -j(~1 s) or socket2-event-driven; on a firefox client flippinginhibitingIdle: true, tag it and applyopacity 1.0 overridevia a tag rule; restore on false. Exact look idle, exact opaque perf during ANY playback (windowed included). Costs a moving part; ~1 s translucent at video start; muted autoplay may not inhibit idle (untested).Rejected: smaller blur size/passes (look degrades, cost still damage-scaled); per-window blur size (doesn't exist on 0.56); global xray (alters every floating window; per-window rule strictly better).
Plan
opacity 1.0 1.0andno_blurlines, addwindowrule = xray on, match:class firefox+windowrule = opacity 0.90 0.93 1.0, match:class firefox. Discord/brave stay opaque.Traps
xrayrequiresnew_optimizations(default on) — do not disable it while 'optimizing'.ignore_opacitydefaults ON — if blur looks missing after restoring opacity, check this before stacking hacks.opacity 1.0 1.0/no_blurlines rather than appending after them — appending before changes nothing.match:classstyle; runhyprctl configerrorsafter every edit.home/.config/hypr/hyprland.confin the repo AND sync live (on the laptop they are separate real files).~/.local/share/ambxst/(generated, rewritten by axctl).ffwarmspecial-workspace pre-warmed Firefox window inherits the same class rules; remember it when counting firefox clients in the phase-3 watcher.set_prop/tagwindowsyntax on 0.56 viahyprctlhelp first; wiki examples are lua-flavored.Verification protocol
Baseline on the current opaque config first, then after each phase, same monitor layout:
for i in $(seq 60); do cat /sys/class/drm/card0/device/gpu_busy_percent; sleep 2; done | awk '{s+=$1} END {print s/NR"%"}'nvidia-smi dmon -s u -c 60(sm + dec columns).uptimeload before/after; confirmhyprctl clients -j | jq '.[] | select(.class=="firefox") | .inhibitingIdle'flips true during playback (validates the phase-3 signal too).