github Psychotoxical/psysonic app-v1.45.0-rc.4
Psysonic v1.45.0-rc.4 Experiment

latest release: app-v1.45.0-rc.5
pre-release7 hours ago

Added

  • Performance Probe (diagnostics) — Modal at Ctrl+Shift+D (sidebar logo is decorative only). Collapsible Phase 1/2 and an open-by-default Phase 3 for the toggles used most in profiling. Flags persist in localStorage, map to data-perf-* on the document root, and can disable targeted subsystems (shell/network hooks, mainstage sections, PlayerBar waveform only, live progress UI, rail artwork, and similar) to isolate WebKit/WebProcess CPU on Linux.
  • Approximate live CPU (Linux) — Tauri command reading /proc (including WebKit helper process names) for rough host CPU share during tuning.
  • Playback progress snapshot APIgetPlaybackProgressSnapshot / subscribePlaybackProgress so time, seekbars, and lyrics can update without writing every tick into the persisted player store.

Changed

  • audio:progress (Rust) — Throttled by minimum interval and position delta, with immediate emit on pause transitions, to reduce IPC during playback.
  • Player store timelinecurrentTime / progress / buffered updates to the persisted store are coarse-grained; live UI reads the snapshot channel instead.
  • WaveformSeek — Still one <canvas> with 2D fillRect bar drawing (same style functions as before; not pre-rendered image layers or drawImage compositing). Progress is fed from the snapshot channel; sparse backend ticks are bridged with prediction/smoothing and capped repaint cadence; animated preview ticks no longer stop solely because the window lost focus while still visible.
  • MPRIS position — Driven from the progress snapshot with conservative timing.

Fixed

  • Hero — Auto-advance respects visibility in the app scroll viewport, pauses when the window is blurred, and recovers after returning on-screen or on focus/visibility changes.
  • Home AlbumRow / SongRail — Artwork visibility budget uses real card geometry so covers fill the viewport without requiring an initial horizontal scroll nudge.
  • Server switch menu — Portaled with fixed coordinates so it stacks above the sidebar.
  • Linux / NixPSYSONIC_ALLOW_NATIVE_GDK skips the default GDK_BACKEND=x11 pin when using the gdk-session wrapper; tauri:dev no longer forces GDK_BACKEND over nix develop defaults.

Added

Audio Preview — Rust Preview Engine and Tracklist Rollout

By @Psychotoxical, PRs #392, #394

Psysonic now has a native Rust-powered preview engine for tracklist previews. Instead of using a separate HTML5 audio path, previews run through a parallel rodio sink on the existing output stream, with dedicated Tauri commands and engine events for preview start, progress and end.

When a preview starts, the main player pauses and only resumes automatically if it was playing beforehand. Starting normal playback, radio playback, resume or stop actions cancels any active preview first, so preview audio and main playback cannot overlap.

The new preview UI is rolled out across the main tracklist surfaces:

  • Albums
  • Playlist detail including suggestions
  • Favorites
  • Artist detail top tracks
  • Random Mix genre and filtered-song lists

Track numbers now stay stable on hover, while dedicated inline Play and Preview buttons handle playback actions from the title cell. Active playing rows keep the equalizer bars, including on hover; active paused rows fall back to a static accent-colored track number. SongRow is intentionally left untouched in this pass.

Settings → Audio now includes a preview section with a master toggle, configurable start position, configurable duration and per-location toggles. Users can keep previews enabled on discovery-heavy surfaces while hiding them on owned-content views. The preview progress ring follows the configured duration automatically.

Preview state is mirrored through a new previewStore, giving the UI one reliable source of truth for preview progress, active state and the currently previewing track metadata.

While a preview is playing, the player bar mirrors the previewed track with cover, title, artist, a dedicated Preview label and an accent top border. Actions that would otherwise target the queued track, such as rating, fullscreen hint and album/artist links, are suppressed during preview playback.

The main play button also reflects preview state with a stop-style preview control and progress ring. Its behavior matches the inline preview buttons: stopping the preview resumes the main player only if it was already playing before. The smaller Stop button uses silent preview stop semantics, cancelling the preview and leaving the main player paused so Stop always means silence.

Spacebar stops an active preview, media keys are ignored during previews, and tray actions cancel the preview before continuing with the requested player action.

Play Next in the context menu now uses a double-chevron icon, making it visually distinct from the Preview button.

The feature includes updated i18n coverage across all supported locales, including the new player-bar preview labels.

Tray — Now Playing Tooltip and Localized Menu Labels

By @Psychotoxical, PR #395, closes #383

The system tray now reflects the current playback state more clearly. On Windows and macOS, the tray tooltip shows the currently playing track as Artist – Title on play, pause and track changes, falling back to Psysonic when nothing is playing.

On Linux, where AppIndicator does not expose a hover-tooltip API, the same now-playing text is shown as a disabled entry at the top of the tray menu instead.

Tray menu labels are now localized across all supported languages, including Play/Pause, Next/Previous, Show/Hide, Exit and the Linux-only empty-state label. The frontend updates the tray labels on startup and whenever the app language changes, without rebuilding the tray icon.

Themes — Kanagawa, Atom One and 1984 Palettes

By @Psychotoxical, PR #390

Open Source Classics gains three new theme families: Kanagawa, Atom One and 1984, adding nine new themes in total.

  • Kanagawa: Wave, Dragon and Lotus
  • Atom One: Dark and Light
  • 1984: Default, Cyberpunk, Light and Orwell

Each theme defines the full token set, including background, accent, text, Catppuccin compatibility, waveform, status and select-arrow tokens. This lets login, queue sidebar and subpages inherit the palette cleanly without component-specific overrides.

The theme picker now groups Open Source Classics by family with dedicated family headings. Theme scheduler dropdown labels are also family-prefixed, making it clearer which palette family a scheduled theme belongs to.

Sidebar Discovery Indicators

By @cucadmuh, PR #397

Sidebar navigation now includes a dedicated unread indicator for New Releases, with persistence per server/library scope and delayed mark-as-seen behavior after opening the New Releases page.

Albums added within the last 48 hours now receive a localized New badge in both album cards and album detail header.

Adaptive Header Controls

By @cucadmuh, PR #397

The top header behavior was reworked for narrow widths: search, Live and Orbit controls now compress in a deterministic order with improved stability in edge-width ranges.

Waveform Wheel Seeking

By @cucadmuh, PR #397

Waveform mouse-wheel seeking now uses fixed step-based jumps with debounce smoothing for more predictable navigation and less jitter during rapid scrolling.

Queue — Drag Outside to Remove

By @cucadmuh, PR #420

You can remove a track from the play queue by dragging its row outside the queue sidebar (main window) or outside the mini player’s queue list. Drop targets still support reordering when you release inside the queue area.

The drag ghost shows a trash affordance only while the cursor is outside the queue bounds; inside the queue it behaves as a normal reorder drag. The mouse-event psy-drop path now carries cursor coordinates so removal can be detected when the drop target is not the queue panel itself.

Statistics — Shareable Top-Albums Card

By @Psychotoxical, PR #425

Statistics page can now export your most-played albums as a shareable PNG, accessible via a share icon next to the Most Played Albums section header. Three aspect ratios for different platforms (Story 9:16, Square 1:1, Twitter Card 16:9), three grid sizes (3×3, 4×4, 5×5), with each cover carrying a thin info strip showing rank + play count.

The card pulls the wordmark and accent color directly from the active theme, so a Catppuccin export looks Catppuccin and a Nord export looks Nord. Cover art reuses the existing IndexedDB cache, so no extra Subsonic round-trips on repeat exports. The header label is hardcoded English ("Top Albums") so a shared image stays legible to followers regardless of their language.

Saving uses the native OS save dialog — no silent dump into Downloads, the user picks the path each time. Data source is local-only (Subsonic getAlbumList(frequent)); Last.fm is intentionally not used. There is no time-window selector because Navidrome's API exposes only cumulative play counts, not per-event play history.

Queue Panel — Position Counter, Tri-State Duration Toggle, Collapsible Now Playing, EQ Indicator

By @kveld9, PR #419

The queue panel got a sweep of UX refinements. The header now shows the current position as (N/M) next to the queue title for at-a-glance context.

The clickable duration label in the header rotates through three modes per click instead of two: total queue time, remaining time, and estimated end-of-queue clock time (e.g. · 02:10). ETA updates every 30 seconds, is formatted in the user's locale, and is visually highlighted with the accent colour while playing.

A new chevron next to the queue title collapses the Now Playing section and queue toolbar, and the collapsed state is persisted across restarts, so users who treat the queue as a pure list can keep it that way.

The currently playing row in the queue list is now indicated by animated equalizer bars to the left of the track title; the bars freeze in place when playback is paused. The previous small play icon next to the title is removed since the EQ bars carry the same signal more clearly.

Shortcuts — Action Registry, Dynamic CLI Help, New Input Targets

By @cucadmuh, PR #435

Shortcut, keyboard, global-hotkey, mini-window and CLI inputs are now all routed through one TypeScript action registry — a single source of truth for what an action does, how it's labelled, and which input transports can fire it. CLI --player help is generated dynamically from the registry, so command coverage stays in sync with the action set automatically.

Nine new input actions were added (requested by zunoz on Discord): start search, start advanced search, toggle sidebar, mute, open / toggle equalizer, toggle repeat, open Now Playing, show lyrics, favorite current track. Help is bound to F1 by default and hidden from the Settings input list; existing users get the F1 binding back-filled into their persisted keybindings on next launch.

Translations for the new action labels follow in a separate i18n nachhol-PR (de, fr, nl, zh, nb, ru, es).

Settings — 3-State Animation Mode (Full / Reduced / Static)

By @Psychotoxical, PR #441, suggested by harumscarum on Telegram

The boolean Reduce animations toggle in Settings → Appearance is now a three-state picker matching the ReplayGain Auto/Track/Album pattern:

  • Full (default for new users): native frame rate, marquee scrolls normally.
  • Reduced (default for users who had the legacy toggle on): 30 fps cap on the animated seekbar wave; the player title marquee runs at half speed.
  • Static: the rAF loop driving the animated seekbar is disabled entirely — the seekbar repaints from the ~2 Hz audio:progress heartbeat only. The player title/artist no longer scroll; long names are truncated with an ellipsis. Lowest GPU/CPU cost of the three.

Existing users with reducedAnimations: true are migrated 1:1 to Reduced on first launch; everyone else lands on Full. The picker is in the same place as before. A contextual hint below the picker explains what the selected mode does.

Tracks — Highly Rated Rail and Per-Card Star Display

By @Psychotoxical, PR #443, prompted by Foxhunter-de in discussion #442

The Tracks page gets a new Highly Rated rail above Random Pick, surfacing your top-rated tracks (sorted by rating, descending). The rail auto-hides on non-Navidrome servers and on libraries with no rated tracks yet. The standard reroll button forces a fresh fetch.

Every song card across the app whose rating is greater than zero now shows a small five-star row below the artist line, filled to the rating value. Read-only display — rating is still done via the row's context menu or the Now Playing star widget.

Backed by an opt-in 60 s in-memory cache for ndListSongs (used only by the new rail; paginated browsing is unaffected). The cache is cleared automatically when you rate a track, switch server, or click the rail's reroll button.

Random Mix — Playlist Size Selector and Filter Panel Cleanup

By @Psychotoxical, PR #445, prompted by Foxhunter-de in discussion #442

Random Mix can now build longer mixes. A new playlist-size picker (50 / 75 / 100 / 125 / 150) sits at the top of the filter panel. Clicking a size button immediately reloads the current mix at the new count — Genre Mix and All Songs both honored, no extra Remix click needed. The choice is persisted across restarts.

The filter panel layout was tightened up at the same time: two sub-headings (MIX SETTINGS and EXCLUSIONS) with a divider between them, and a slightly larger panel-level header so the visual hierarchy reads cleanly. A small italic note below MIX SETTINGS explains that large mix sizes may return fewer unique tracks if the server's random pool runs short.

Under the hood, fetchRandomMixSongsUntilFull now scales batch size, max-batch ceiling and dup-streak budget with the requested target — so a 150-track mix can finish in a single round-trip on most libraries instead of stalling out at ~120.

UI — Bulk Entity Ratings, Random Albums Multi-Select, Album New Badge

By @cucadmuh, PR #446

Multi-album and multi-artist context menus now include a shared star-rating row for the current selection (mixed ratings show empty until you set a value; keyboard navigation supported), with new aria-label strings across locales. Random Albums passes the active selection into each AlbumCard so the same bulk context menu works from the roll grid. The album New badge moves to the top-right of the cover and stacks with the offline badge so the two no longer overlap.

NixOS — Flake: X11-wrapped default vs session GDK

By @cucadmuh, PR #447

The flake exposes two Linux installables: psysonic / default pins GDK_BACKEND=x11 for a stable GTK/WebKit stack on mixed Wayland setups; psysonic-gdk-session drops that override so GDK follows the session (native Wayland where the stack supports it). nixos-install.md documents trade-offs and zsh-safe quoting for nix run 'github:…#…' URLs.

Fixed

  • Settings → Audio no longer blanks the app on macOS (Issue #382, PR #384, by @Psychotoxical): Fixed a macOS-only crash where opening Settings → Audio could turn the whole app into a blank window. The Equalizer canvas now waits until it has valid layout dimensions before drawing, and redraws automatically once the section is visible.

  • Polish (PR #397, by @cucadmuh): multiple branch-local interaction fixes around sidebar drag/drop behavior, Live dropdown layering, queue-resize handle behavior during scroll/overlay-scrollbar interaction, and now-playing narrow-layout stability.

  • Track preview audio in sync with progress ring; huge files no longer abort (Issue #421, PR #423, by @Psychotoxical): Previews used to start audio about 25 % into the preview window on mid-track starts because Sink::try_seek ran in parallel with sink.append while the 30 s take_duration cap was already counting wall-clock from append. The seek now runs on the bare source before append, and the progress-ring animation only starts once the engine actually emits audio:preview-start — a small loading spinner is shown during the download/decode/seek warmup. The preview HTTP-client timeout was raised from 30 s to 5 min, so multi-hundred-megabyte Hi-Res files no longer abort the download mid-fetch.

  • Windows playback stutter under GPU load (Issue #334, PR #426, by @Psychotoxical): Audio could stutter and crackle on Windows whenever another app put GPU/CPU pressure on the system (browser, 3D apps, games). The WASAPI render thread is now promoted to MMCSS "Pro Audio" via AvSetMmThreadCharacteristicsW, so it survives priority contention from competing graphics work. Reproed and validated under a Half-Life parallel-load stresstest. Companion mitigations for high-GPU situations: cosmetic UI animations now pause when the window loses OS focus, and a new Reduce animations toggle in Settings → Appearance caps animated seekbar styles (pulsewave, particletrail, liquidfill, retrotape) to 30 fps for users on GPU-constrained machines (off by default).

  • Linux dev — sidebar and main content invisible after HMR (PR #434, by @Psychotoxical): The data-app-blurred="true" CSS rule introduced in #426 used a * selector to pause every animation while the window was unfocused. On WebKitGTK + no-compositing this triggered a stale rendering bug after Vite hot-reloads — the sidebar and main content stayed unpainted until any user interaction nudged a re-render. The rule now targets only the concrete heaviest infinite animations (eq bars, marquees, now-playing dot pulse, fullscreen mesh blob / portrait, .spin); release builds are unchanged in behaviour.

  • Mono playback (right channel only) after natural track end with gapless OFF (PR #439, by @Psychotoxical): When gapless playback was disabled and a track ended naturally, the next track could play only on the right channel for the rest of its duration. The 500 ms track-separation silence prepended in this exact transition was built with Zero + take_duration, whose integer-nanosecond math at 44.1 kHz / 2 ch leaks half a frame (44103 samples instead of 44100), shifting the next source's L/R parity in the device frame stream. Replaced with a frame-aligned SamplesBuffer. Manual skip and album-first-play were unaffected because they bypass the silence prepend. Independently identified by xrexy on Discord while the diagnosis was landing here.

Don't miss a new psysonic release

NewReleases is sending notifications on new releases.