Skip to content

Restore translucent+blurred Firefox look without the video-playback performance hit #21

Description

@nethum529

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

  1. 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.
  2. 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.
  3. 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).
  4. 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:

  1. Same 1080p60 YouTube video, windowed, on the AMD-driven monitor, 120 s.
  2. 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"%"}'
  3. dGPU: nvidia-smi dmon -s u -c 60 (sm + dec columns).
  4. uptime load before/after; confirm hyprctl clients -j | jq '.[] | select(.class=="firefox") | .inhibitingIdle' flips true during playback (validates the phase-3 signal too).
  5. Repeat fullscreen — must match the opaque baseline exactly (validates the third opacity value).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions