server2 ba060f414c glass: blur strength as a config value
shell.conf blur (1 to 30, default 3; Hyprland's default is 8) sets how
much Hyprland frosts what is behind the glass, applied the moment
shell.conf is saved. Hyprland's blur strength is global (a layer rule can
only switch blur on), so the shell sets decoration.blur.size through
hyprctl eval, and again on every configreloaded>> event, since a config
reload puts Hyprland's own value back. leaving blur out keeps Hyprland's
2026-10-10 00:24:48 +02:00
2026-10-06 03:28:55 +02:00

Magic Island: an Amane media widget

A Dynamic-Island-style media widget for Amane (Rust Wayland shell library), running on Hyprland. It sits as a small pill at the top centre of the screen and opens into a now-playing card.

  • Pill: cover thumbnail, title, 7 live bars from the shown player's own sound (lows left, highs right, growing up and down from the middle), or a pause sign. With no player open, an idle pill: a music note and "Nothing playing".
  • Card: a still CD of the cover ringed by 40 cava "candles", title and artist, a seekable time bar, previous / play-pause / next.
  • Player switcher ("VIA …" chip): every MPRIS player; picking one pins it, otherwise the island follows whichever plays.
  • Output switcher: moves only the shown player's audio to another device.
  • Per-player volume (scroll the volume chip, click to mute), set on that player's own PulseAudio/PipeWire streams, so it also works for browsers that ignore MPRIS volume.
  • Real 10-band equalizer per player (31 Hz to 16 kHz, ±12 dB): a PipeWire filter-chain per player, with 8 presets (Flat, Bass, Treble, Vocal, Pop, Rock, Jazz, Classic). Curves are saved per player.
  • Background: a diagonal gradient in the wallpaper's colours (see Colours); optionally the open card takes the song cover's colours.
  • Opening animation: the pill fades out, the empty shape grows, then the card fades in. Closing runs backwards. Moving the pointer off the open card closes it.
  • Every monitor: each monitor shows the pill. The card opens only on the monitor you click (a keybind opens it on the focused one), and only the focused monitor's pill shows live bars; the others show still bars, which keeps the cost at about one island's.
  • Workspace pill: top left on every monitor, that monitor's Hyprland workspaces as round numbered slots in the island's look. The focused workspace takes the accent colour, the one shown on another monitor a softer accent, empty ones dim. Click one to switch to it.

Colours

The shell takes its colours from the wallpaper through matugen. Skwd-wall v2 runs matugen on every new wallpaper and renders matugen/amane.colors into ~/.config/amane/theme; the shell picks the file up the moment it changes (inotify through amane::watch_file) and cross-fades to the new colours. Without the file it keeps its original pink on purple; deleting it takes effect at the next start.

Register the template in Skwd-wall as a matugen integration:

  • template: ~/.config/amane/matugen/amane.colors
  • output: ~/.config/amane/theme

The Material You scheme type is Skwd-wall's setting (matugen.schemeType).

shell.conf (read live, no restart):

cover_colors = false   # true: the open media card and equalizer take the song cover's colours as background
transition_ms = 400    # how long a colour change cross-fades; 0 switches instantly
glass = 1.0            # surface opacity, 0.05 to 1; below 1 the desktop shows through
blur = 3               # Hyprland's blur strength behind the glass, 1 to 30 (Hyprland default 8)

Glass

Below glass = 1.0 the surfaces are see-through. Amane can only blur what the shell draws itself, so the frosting has to come from Hyprland: add this layer rule to the Hyprland config (hyprland.lua). The namespace match is a full match, and ignore_alpha keeps the transparent margin around the island from being blurred too.

-- frosted glass behind amane's island and workspace pills
amane_glass = hl.layer_rule({
    name = "amane-glass",
    match = { namespace = "^magic-.*$" },
    blur = true,
    ignore_alpha = 0.2,
})

The look of the surfaces (fill, rim, highlight, shadow) lives in src/surface.rs.

blur in shell.conf sets how much the glass frosts: low values leave the background nearly readable. Hyprland's blur strength is one global setting (a layer rule can only turn blur on), so the shell sets decoration.blur.size for everything Hyprland blurs, and sets it again after every Hyprland config reload. Leave blur out to keep Hyprland's own value.

Layout

src/main.rs        app setup: Fira Code font, the `island` IPC handler, palette from the cover
src/island.rs      the view: pill, card, ring, equalizer, dropdowns, animation, IPC commands
src/sound.rs       per-player volume, mute, output and equalizer (pactl, pw-cli, pipewire)
src/spectrum.rs    the shown player's sound as 24 levels (pw-record | cava)
src/art.rs         cover download (curl) and the palette file
src/workspaces.rs  the workspace pill, top left on every monitor
src/theme.rs       the shell's colours: the matugen theme file, shell.conf, the cross-fade
src/surface.rs     what the island and pills are made of: fill, rim, highlight, shadow
src/parse.rs       pure text parsing with unit tests, no amane dependency
matugen/           the matugen template (amane.colors), rendered by skwd-wall into ./theme
shell.conf         settings: cover_colors, transition_ms
island-eq          saved equalizer curves (a snapshot of ~/.config/amane/island-eq)
tools/             remote test driver and fake MPRIS players, see AGENTS.md

Requirements

On the desktop that runs the shell:

  • the amane CLI and Rust toolchain (amane compile builds src/)
  • PipeWire with pipewire-pulse: pactl, pw-record, pw-cli, pipewire (filter-chain module)
  • cava, curl
  • the Fira Code font

Install and run

The shell lives in ~/.config/amane/. Copy (or clone) this repo's src/ there, then build and start:

amane compile
~/.cache/amane/project/target/debug/amane-shell

The installed CLI on the current desktop is older than the Amane docs: it doesn't save ~/.cache/amane/amane-shell, so amane run has nothing to start. Run the binary above directly.

Control it

amane ipc call island toggle           # also: open, close
amane ipc call island next             # show the next player
amane ipc call island play-pause       # play/pause the shown player; the icon flips at once
amane ipc call island menu output      # open a dropdown: output | player | close
amane ipc call island preset Rock      # apply an equalizer preset to the shown player
amane ipc call island output <sink>    # move the shown player to a sink (pactl sink name)
amane ipc call island status           # player=<identity> expanded=<bool> menu=<bool>

Example Hyprland keybind:

bind = SUPER, M, exec, amane ipc call island toggle

Runtime files

  • ~/.config/amane/island-eq: one line per player identity, <player>\t<preset or Custom>\t<10 gains in dB>.
  • $XDG_RUNTIME_DIR/magic-island/: downloaded covers, palette-cover, cava.conf, eq-<key>.conf. On tmpfs, gone at logout.
  • Equalizer nodes are named magic_island_eq.<key>. The key is the player's MPRIS bus name without org.mpris.MediaPlayer2., non-alphanumerics as _ (e.g. chromium_instance123).

Known limits

  • The time bar moves in 1 s steps (the MPRIS poll rate).
  • Every slider step starts one pactl or pw-cli process.
  • An equalizer keeps running, and keeps that player routed through its last curve, if the shell stops while the player is open.
  • The candle ring around the CD is still mirrored left against right; the pill's bars are not.
  • Dropdowns have no keyboard navigation.
S
Description
Config for amane
Readme 5.2 MiB
Languages
Rust 94.5%
WGSL 2.3%
Python 2.1%
Shell 1.1%