Desktop Spotify client built with Rust and Qt Quick, with a Material 3 Expressive interface.
Windows and Linux; macOS is experimental.
Download · Screenshots · Features · Limitations · Installation · Building · Data and privacy
Materialfy replaces the official Spotify desktop app for listening to music in a regular native window. The interface is written in QML (Qt 6.12) with no browser engine, and the logic is in Rust. Audio is played by librespot inside the app's own process, so playback on the computer requires Spotify Premium. Interface colors follow the cover of the current track. The interface follows the system language: Russian on a Russian system, English otherwise; Settings → Language can fix either one.
The project is assembled from two MIT-licensed projects: the Spotify side comes from spotifast (crmne), the interface from yamusic-md3 (yaskorinov). See Origin and license for details.
Ready builds of every version are on the Releases page. Take the file for your system from the latest release:
| System | File |
|---|---|
| Windows 10/11, x64 | Materialfy-<version>-Setup.exe (installer, no admin rights needed) or Materialfy-<version>-windows-x64-portable.zip |
| Linux x86_64 | Materialfy-<version>-x86_64.AppImage or materialfy-<version>-linux-x86_64.tar.gz |
| Linux aarch64 | Materialfy-<version>-aarch64.AppImage or materialfy-<version>-linux-aarch64.tar.gz |
| macOS 13+, Apple silicon | Materialfy-<version>-macos-arm64.dmg (experimental) |
| macOS 13+, Intel | Materialfy-<version>-macos-x86_64.dmg (experimental) |
SHA256SUMS.txt in the same release lists the checksums. Starting with 0.2.1 the app checks Releases for new
versions on its own (Settings → Updates). The installer, the portable folder and the AppImage download and
apply an update in place; on macOS the new .dmg opens; a copy from tar.gz only links to the release page.
Taken with a Premium account and music playing; the colours follow the cover of the current track.
| The mini player: the window switches to it when it is narrower than 480 or shorter than 400 pixels. |
- Sign-in through the browser with OAuth PKCE. You type your password on the Spotify website; the app only
receives a one-time code on
127.0.0.1. - Playback on this computer through librespot (Premium required): 96, 160 or 320 kbit/s, volume normalization, gapless playback, crossfade up to 12 seconds, speed from 0.5× to 2× without changing pitch, similar music after an album or playlist ends, output device selection, and an audio cache from 256 MB to 10 GB.
- The computer shows up in Spotify Connect under the name set in Settings. You can move playback to a phone or a speaker and back; while music plays on another device, the window works as a remote with that device's volume. Speakers on the local network that are not signed in yet are found over mDNS and can be connected from the devices menu.
- Queue: a manual queue kept separate from the context, "Play next", dragging tracks into the queue and reordering rows, removal with undo.
- Radio from a track, artist, album or playlist, and from any track in search results. A station can be regenerated or saved as a playlist.
- Library: playlists with folders, pinning to the top (in this app only), Liked Songs, albums, artists, podcasts. Grid or list view, sorting.
- Editing your own playlists: name, description, cover, public or private, reordering tracks with the mouse, removal with an Undo button, adding tracks from recent, liked and search, creating and deleting playlists.
- Search across tracks, albums, artists, playlists and podcasts. An
open.spotify.comlink can be pasted or dropped onto the window. - Artist page with monthly listeners, biography, popular tracks and releases. The New releases page collects recent releases from artists you follow.
- Podcasts: show and episode pages, listening progress, chapters, skip back 10 seconds and forward 30.
- Synced lyrics. The app takes lyrics from Spotify first and falls back to LRCLIB, NetEase and Musixmatch. Three styles to choose from (classic, Spicy, karaoke), text size and background scrim.
- Material You color scheme from the current track's cover or from a chosen accent color, light and dark themes. Animations can be shortened or follow the system setting.
- Three window layouts: full, compact for a narrow window, and a mini player with an "Always on top" option (not available on Wayland).
- Its own window frame on every OS: Material 3 minimize, maximize and close buttons, rounded corners (on Windows 11 drawn by the system; Windows 10 keeps square ones), square corners when maximized.
- Media keys, the SMTC tile on Windows and MPRIS on Linux, a tray icon with minimize-on-close, single running instance.
- Interface in English, Russian or Portuguese (Brazil). By default it follows the system language; Settings → Language switches it.
- Proxy: system, HTTP or SOCKS5, with username and password. Sign-in, Web API, playback, lyrics and images all go through it.
- librespot only works with Premium accounts. With a free account music does not play on the computer, and radio, which is built through librespot, is blocked. The Spotify Web API also allows controlling other devices only with Premium. Without it you still have the library, search and playlist editing.
- Spotify serves librespot up to 320 kbit/s Ogg Vorbis, so there is no lossless.
- Crossfade and speed only work when playing on this computer. They are not passed to a speaker or phone over Connect, and other Spotify devices see the track position with corrections once per second.
- Tracks cannot be downloaded for offline listening; the app does not bypass DRM. The audio cache only speeds up repeat playback.
- The app does not update itself; a new version has to be installed manually.
- The shared Web API Client ID is one for all users of the app, and Spotify often rate-limits it. That is why the app goes through your Spotify session first and keeps the Web API as a fallback. Details and how to set up your own Client ID are in Spotify session and Web API.
- Some limits come from the Web API itself. The context queue shows about 20 upcoming tracks, because the API does not return the total. Removing a track from a playlist removes all of its duplicates, because the delete method does not accept a position. Spotify history is limited to the last 50 plays.
- The app is unofficial. Spotify may restrict third-party clients, and you are responsible for how your account is used.
- A Spotify account. Premium is needed for audio on this computer and for radio.
- Windows 10 1809 or later (x64); Linux x86_64 or aarch64; macOS 13 or later (the build is experimental; media keys on macOS are untested).
- Free local ports 8989 and 8898: Spotify sends the browser back to them after sign-in. If the firewall asks,
allow connections on
127.0.0.1.
The quickest way is a ready build from Download. To build from source instead:
You need Rust, Qt 6.12 and Build Tools for Visual Studio (details in Building). The script builds the app and the installer:
git clone https://github.com/fatyzzz/materialfy
cd materialfy
.\build_windows.ps1 -QtDir C:\Qt\6.12.0\msvc2022_64target\dist\ will contain Materialfy-<version>-Setup.exe and a portable materialfy\ folder. The installer
does not need administrator rights and installs into %LocalAppData%\Programs\Materialfy.
git clone https://github.com/fatyzzz/materialfy
cd materialfy
./install.shThe script works with apt, pacman, dnf and zypper. It first shows which system packages it will install with
sudo and waits for confirmation. Rust is installed with rustup into ~/.cargo. If the system has no Qt 6.12
with QML modules, the script downloads one next to the sources, about 250 MB. The first build takes 5 to 15
minutes; after that you get the ~/.local/bin/materialfy command and an entry in the application menu. Running
the script again updates the app.
./install.sh --uninstall # remove the command and the menu entry; settings, data and sign-in are keptThere is no installer; build the .app as described in Building.
Spotify grants two separate permissions, and the app asks for them one at a time.
- Account sign-in: Account → Sign in with Spotify. A browser opens, and after you confirm, Spotify sends it
back to
http://127.0.0.1:8989/login. This is enough for the library, search and playlists. - Playback on this computer: the Playback screen or the devices menu. The second confirmation in the browser
comes back to
http://127.0.0.1:8898/login. After that the computer appears in Spotify Connect.
Without the second step Materialfy works as a remote for other devices (with Premium).
The app has two ways to get Spotify data.
- The librespot session. This is the same protocol the official app uses; it becomes available after the second sign-in step (playback on this computer). The session runs under your account, and Web API quotas do not apply to it.
- The Web API (
api.spotify.com) through a Client ID. By default this is the app's shared Client ID, one for all of its users. Spotify often answers it with 429 ("too many requests") and sometimes withQUOTA_EXCEEDED, in which case the Web API does not respond at all until the quota resets, which can take up to a day.
Reads go through the session first; the Web API is asked only when there is no session, it did not answer in time, or it does not support that request. The session currently serves playlists (the list, their contents, editing tracks), Liked Songs, saved albums and artists, album, artist and track pages, search and New releases. Still Web API only: podcasts in the library and their pages, listening history, the home shelves "Top" and "Made for you", radio seed labels, creating a playlist, changing its name or cover, following someone else's playlist, and remote control of another device (commands and polling its state).
To make the shared Client ID last longer, the Web API client:
- splits requests into three queues: the page you opened, background refresh, and player state polling. Background gets up to two thirds of the budget (the shared key allows 40 requests per 30 seconds), polling a quarter, pages all of it;
- merges identical requests already in flight and keeps GET responses for 20 seconds (any write clears the cache and keeps it from filling for a minute);
- after a 429 or an exhausted quota, keeps background and polling off the network, while pages retry every 15 seconds, or every minute for a quota. Player polling waits longer after each consecutive 429: one minute, two, four, up to ten minutes.
While a limit is active, Settings → Account shows a "Web API limited" notice with an estimated end time.
If the shared key keeps hitting limits, register your own Spotify app. Its limits are shared only with you.
- Open developer.spotify.com/dashboard, sign in and click "Create app".
- Any name and description will do. Add
http://127.0.0.1:8989/loginto Redirect URIs and tick Web API in the API section. Save. - A new app runs in development mode and only lets in users from its list. Add the email of your Spotify account under "User Management".
- Copy the Client ID (32 characters) from the app page.
- In Materialfy: Settings → Account → Personal Spotify app → Set up. The form shows the redirect URI from step 2 with a copy button. Paste the Client ID, click Save and confirm access in the browser.
Even with your own Client ID, some Web API requests go through the shared one: other people's playlists, the list of your playlists, playlist search, and popular tracks and related artists on the artist page.
| What | Windows | Linux | macOS |
|---|---|---|---|
| Settings | registry HKCU\Software\materialfy\Materialfy |
~/.config/materialfy/Materialfy.conf |
~/Library/Preferences/materialfy/Materialfy.conf |
| Data (player session, history) | %APPDATA%\materialfy\Materialfy |
~/.local/share/materialfy/Materialfy |
~/Library/Application Support/materialfy/Materialfy |
| Cache (audio, covers, lyrics) | %LOCALAPPDATA%\materialfy\Materialfy\cache |
~/.cache/materialfy/Materialfy |
~/Library/Caches/materialfy/Materialfy |
| Tokens and proxy password | Credential Manager | Secret Service (keyring) | Keychain |
Secrets are kept only in the system store under the name materialfy. Sign out in Settings removes all Spotify
permissions from the store. The cache stays; it can be cleared in Settings → Storage.
The app has no telemetry or usage statistics of its own. Network requests go to Spotify servers (sign-in, Web API, playback, covers) and to the lyrics services LRCLIB, NetEase and Musixmatch. On the local network the app looks for Spotify Connect devices over mDNS.
You enter your Spotify password only on accounts.spotify.com in your own browser. The app requests access to
the library, playlists and playback control, and reads your plan (Free or Premium) through user-read-private.
It does not ask for access to your email.
| Keys | Action |
|---|---|
| Space | play or pause |
| Ctrl + → / ←, media keys | next or previous track |
| Ctrl + ↑ / ↓ | volume |
| Shift + → / ← | seek 10 seconds |
| Ctrl + P | full-screen player |
| Ctrl + L | lyrics |
| Ctrl + F | search in the list; press again for global search |
| Ctrl + Shift + L | library |
| Ctrl + B | collapse or expand the sidebar |
| Ctrl + , | settings |
| Alt + ←, mouse back button | back |
| F11 | full screen |
| Ctrl + Q | quit on Linux (⌘ + Q on macOS) |
Common requirements:
- Rust 1.98 or later (rustup; on Windows the
stable-x86_64-pc-windows-msvctoolchain). - Qt 6.12 with the qtdeclarative, qtsvg, qtshadertools and qtimageformats modules. It can be installed with the
Qt Online Installer or with
install_qt.py, the script CI uses: it downloads archives from download.qt.io and verifies checksums. - Python 3 for
install_qt.py.
cxx-qt finds Qt through the QMAKE variable (the path to qmake6). The built app looks for QML in this order:
$MATERIALFY_QML_DIR, then qml/ next to the executable, then src/materialfy/qml in the repository, so
cargo run works from the source tree.
Build with --locked. Cargo.lock pins vergen 9.0.6; version 9.1 breaks the librespot-core build script.
Do not run cargo update without -p.
You need Build Tools for Visual Studio 2022 with
the "Desktop development with C++" workload, Rust (msvc), Qt 6.12 msvc2022_64, PowerShell 7, and, for the
installer, Inno Setup 6 (winget install JRSoftware.InnoSetup).
python .github\scripts\install_qt.py --out C:\Qt\6.12.0\msvc2022_64 # if Qt is not installed yet
.\build_windows.ps1 -QtDir C:\Qt\6.12.0\msvc2022_64build_windows.ps1 runs cargo build --locked --release -p materialfy, assembles the exe,
qml\, assets\, the Qt libraries (through windeployqt) and the MSVC runtime in target\dist\materialfy\, then
calls Inno Setup if it finds it. Without -QtDir, the script looks for Qt in $env:QTDIR, next to
$env:QMAKE, in C:\Qt\6.12.0\msvc2022_64 and in ~\Qt\6.12.0\msvc2022_64.
| Flag | What it does |
|---|---|
-SkipInstaller |
skip the Inno Setup step |
-Portable |
also produce Materialfy-<version>-windows-x64-portable.zip |
-SkipBuild |
use an existing target\<profile>\materialfy.exe |
-CargoProfile debug |
debug build |
-Version 1.2.3 |
installer version (taken from Cargo.toml by default) |
-OutDir DIR |
output directory (target\dist by default) |
For development without packaging:
.\run_windows.bat --new-instance # cargo run --release with the Qt it finds; arguments go to the appor by hand:
$env:QMAKE = "C:\Qt\6.12.0\msvc2022_64\bin\qmake6.exe"
$env:PATH = "C:\Qt\6.12.0\msvc2022_64\bin;$env:PATH"
cargo run --locked -p materialfyThe easiest way is ./install.sh (see Installation). By hand on Debian or Ubuntu:
sudo apt install build-essential pkg-config lld python3 7zip libasound2-dev libpulse-dev \
libgl-dev libegl-dev libxkbcommon-dev libfontconfig1-dev libdbus-1-dev libxcb-cursor0 libxkbcommon-x11-0
python3 .github/scripts/install_qt.py --out ~/Qt/6.12.0/gcc_64
export QMAKE=~/Qt/6.12.0/gcc_64/bin/qmake6 LD_LIBRARY_PATH=~/Qt/6.12.0/gcc_64/lib
cargo run --locked --release -p materialfyOn older Ubuntu releases the 7zip package is called p7zip-full. On Arch:
sudo pacman -S base-devel lld alsa-lib libpulse 7zip. cxx-qt does not link with GNU ld.bfd; you need lld,
mold or gold.
The AppImage and tar.gz are built by package_linux.sh; the output goes to
target/dist/:
bash .github/scripts/package_linux.sh 0.1.0brew install sevenzip
python3 .github/scripts/install_qt.py --host mac --out ~/Qt/6.12.0/macos
export QMAKE=~/Qt/6.12.0/macos/bin/qmake6
cargo build --locked --release -p materialfy
bash .github/scripts/package_macos.sh 0.1.0 # target/dist/Materialfy.app and .dmg| Flag | What it does |
|---|---|
--offline |
do not restore the sign-in from the secret store |
--new-instance |
start a second instance even if the app is already open |
--light |
light theme |
--size 1280x800 |
window size |
--screenshot shot.png, --delay 1500 |
save a screenshot of the window after the given number of milliseconds and exit |
--config-dir DIR |
separate settings directory |
--set key=value |
initial window property, for example --set startPage=settings |
--gallery |
Material 3 component gallery |
Environment variables: MATERIALFY_QML_DIR sets a custom QML directory, MATERIALFY_SESSION_FILE sets the
player session file, RUST_LOG controls logging (for example RUST_LOG=materialfy=debug,sf_player=debug).
cargo test --locked --workspacebuild.yml runs on pull requests to main and manually. The Windows build (tests,
installer, portable archive, a check screenshot of the window) is required. Linux and macOS are built on a
best-effort basis; their failure does not fail the whole run.
The interface is written in QML; the Material 3 Expressive components live in src/materialfy/qml/Md3. The Rust
core is connected to QML through cxx-qt. Async code runs in a single tokio
runtime, and the Qt UI thread receives ready-made state snapshots.
| Crate | What it does |
|---|---|
md3-model |
data types for QML: tracks, albums, playlists, devices |
sf-auth |
OAuth PKCE sign-in, storing permissions in the system secret store, proxy |
sf-api |
Spotify Web API client: priority queues, GET merging and caching, cooldown after 429 and quota errors |
sf-player |
librespot and Spotify Connect, queue, reconciling local and remote playback |
sf-services |
sign-in, library, catalog, radio, home, lyrics, devices, history |
mf-theme |
Material You schemes from a cover or a chosen color |
mf-lyrics |
lyrics from several sources, LRC parsing, cache |
mf-platform |
directories, settings, MPRIS, single instance |
mf-i18n |
interface translation: English strings in the code, Russian and Portuguese dictionaries from i18n/ru/*.json and i18n/pt/*.json |
materialfy |
the executable: QML bridges, command-line flags, image cache, window frame on Windows |
Materialfy is based on two MIT-licensed projects:
- spotifast (Carmine Paolino, crmne): sign-in, Web API client, playback
through librespot, Spotify Connect, queue, library and radio (
crates/sf-*). - yamusic-md3 (yaskorinov and contributors): the QML interface and
the Material 3 Expressive component library, the cxx-qt shell, cover-based theming, lyrics, platform services,
build scripts (
src/materialfy/qml,crates/materialfy,crates/mf-*).
Third-party components: librespot (MIT, our own fork fatyzzz/librespot with crmne's patches), fastframe (MIT, audio output and media keys), cxx-qt (MIT or Apache-2.0), Qt 6 (LGPL-3.0, linked dynamically), the Google Sans font (SIL Open Font License 1.1) and Material Symbols Rounded icons (Apache License 2.0).
Materialfy is licensed under the GNU General Public License, version 3 or any later version
(GPL-3.0-or-later); the text is in LICENSE. Releases up to and including 0.2.1 were published under the
MIT License, and the code taken from spotifast and yamusic-md3 keeps its MIT notice in
LICENSES/MIT.txt. Authors, sources and dependency
licenses are listed in NOTICE. The "Wave-M" logo is in docs/brand/.
Materialfy is not affiliated with or endorsed by Spotify AB. Spotify is a trademark of Spotify AB.