Spotifast Rebuilds Spotify in Rust, Then Runs It on Two Identities at Once

A native egui client with no browser engine and a fraction of the memory. Its real trick is a runtime switchboard that picks the right Spotify app for every single API call.

8 min read • View on GitHub • More from crmne

A wide desk scene drawn in black ink. A slim laptop on the left displays a small, tidy application window while a comically enormous web-browser-shaped hot-air balloon strains upward on the right, tethered to the desk by a single thin line. A tape measure stretches between them. The illustration contrasts a lightweight native app against the browser-engine bloat of the official Spotify client.
Spotifast trades an embedded browser engine for a small native window. The balloon is the thing it left behind.
Key Takeaways

The 600MB Problem

The official Spotify desktop client is a web application wearing a desktop costume. It ships an embedded browser engine, a full rendering stack, and enough background machinery that playing one song can cost between 600MB and 1GB of RAM.

Spotifast does the same job in 100 to 250MB. It is a native Rust application built on egui, an immediate-mode GUI toolkit, with librespot handling the audio. No browser engine. No Chromium. No web view. The project's own README and website publish those memory figures, so treat them as the author's claims rather than an independent benchmark.

The tagline is three words long.

Spotify, native and fast.

Fastpotify project, Project tagline · crmne/fastpotify

That is the pitch. Everything interesting lives in what the pitch leaves out.

The Trick Is Not the Renderer

Drawing a music player in egui is not the hard part. Playlists, search results, an album grid, a queue: these are solved problems, and dozens of projects have solved them.

The hard part is that Spotify will not let one app do everything.

Development Mode, Spotify's sandbox for registered applications, restricts what any single identity can read. A personal app registered by a developer gets tighter quotas and narrower access than Spotify's own shared public app. Some operations are only legal from one identity. Some are only legal from the other. A client that wants to browse a library, edit playlists, and search the catalog has to be more than one app.

Spotifast's answer is to register two identities and decide, per request, which one is allowed to answer.

Two Apps, One Client

The code names them SHARED and PERSONAL.

SHARED is a public Web API application. The source comments say its client ID is shared with spotify-player, ncspot, and Omarchy Spotify. PERSONAL is the user's own registered Client ID, supplied during sign-in.

The two identities are not interchangeable. Each carries its own limits. The SHARED profile allows search_limit: 20 and artist_albums_limit: 50. The PERSONAL profile allows 10 and 10. Spotify constrains personal apps more tightly, so the shared identity is less a fallback than a wider door.

Every API call passes through one pure function, plan(), which returns either Shared or Personal.

The plan() gauntlet. Toggle personal_ready off and watch the routing collapse to a single destination. Cycle a playlist's access and watch it flip.

The function is a match statement. It reads like a policy document.

fn plan(operation: Operation, personal_ready: bool) -> ApiSource {
    match operation {
        CanonicalAccount
        | PlaylistLibrary
        | PlaylistSearch
        | ArtistTopTracks
        | UnsupportedDevelopmentMode => Shared,

        PlaylistMetadata(External | Unknown)
        | PlaylistItems(External | Unknown)
        | PlaylistMutation(External | Unknown) => Shared,

        PlaylistMetadata(_) | PlaylistItems(_) | PlaylistMutation(_)
            if personal_ready => Personal,

        Playback | UserData | PlaylistCreation | Catalog | CatalogSearch
            if personal_ready => Personal,

        _ => Shared,
    }
}

How a Request Picks a Side

The input to plan() is an Operation, an enum with about 13 variants: CanonicalAccount, Playback, UserData, PlaylistLibrary, PlaylistSearch, PlaylistMetadata, PlaylistItems, PlaylistMutation, PlaylistCreation, ArtistTopTracks, Catalog, CatalogSearch, and UnsupportedDevelopmentMode.

Most of these route the same way every time. CanonicalAccount, PlaylistLibrary, PlaylistSearch, and ArtistTopTracks always go to SHARED. They are operations the shared identity can serve.

The playlist operations are different. They carry a PlaylistAccess value, and that value changes the destination.

PlaylistAccessMeaningDestination
OwnedThe signed-in user owns the playlistPersonal, when personal_ready
CollaborativeShared editing, user is a memberPersonal, when personal_ready
ExternalOwned by someone elseShared
UnknownOwner not yet knownShared, then re-routed once the owner is revealed

The Unknown case is the interesting one. When Spotifast does not yet know who owns a playlist, it cannot know which identity is allowed to read it. So it sends the request to SHARED, which can serve anything. Once the response reveals the owner, the gateway re-routes subsequent calls.

The routing decision is not made once at startup. It is made per request, with information that arrives mid-flight. Spotifast is not a load balancer for traffic. It is a load balancer for permissions.

You Sign In Twice, On Purpose

The same constraint shows up in authentication.

auth.rs implements OAuth 2.0 Authorization Code with PKCE. The module comment is explicit about the security boundary: the browser handles password entry, and the local process only ever sees the one-time authorization code Spotify sends back to a loopback listener.

The central abstraction is a Grant, which bundles a client_id, a redirect_port, and a list of scopes. There are three constructors.

The PKCE details are careful. begin() generates a 48-byte random verifier, derives an S256 challenge, and adds an 18-byte state value. The verifier never touches the browser.

The loopback port is reserved before the browser opens. listen_for_redirect binds the socket first and fails with an actionable message if the port is taken, which prevents a race where the browser returns to a closed port. The listener then waits for the code with a 10-minute timeout and a cancel signal.

Scopes are minimized on purpose. The Web API sign-in requests user-read-private so the app can detect whether the account is Free or Premium, which decides whether local playback is offered. No email.

The Winamp Skin in the Machine

For all the policy machinery, Spotifast is also a love letter to desktop music software.

It loads Winamp 2 .wsz skins. It has an equalizer. It has MilkDrop visualizations. There are dedicated winamp, skin, eq, and vis modules. The project ships a sample skin and an Omarchy theme template. On macOS there is a menu bar integration and a notch widget. On Windows there is a taskbar thumbnail toolbar.

None of this is necessary. All of it is the point.

An overhead close-up of a mailroom sorting desk, drawn in black ink. A pair of hands slides envelopes into two wooden pigeonholes tagged SHARED and PERSONAL, each envelope stamped with a small symbol. One envelope sits half-turned, as if pulled back for re-sorting. The scene renders Spotifast's gateway routing as a physical act of sorting, with the half-turned envelope standing in for a playlist whose ownership is discovered mid-request.
Every API call gets sorted. The half-turned envelope is a playlist of unknown ownership, pulled back once the gateway learns who owns it.

Where It Fits

Spotifast sits between two poles. On one side, Spotify's official client: fully supported, fully featured, and heavy. On the other, terminal clients like spotify-player, ncspot, and spotify-tui: light, fast, and keyboard-driven.

ProjectInterfacePlayback engineBrowser enginePlaylist editing
SpotifastNative GUI (egui)librespotNoYes
Spotify officialOfficial GUIProprietaryYesYes
spotify-playerTUIlibrespotNoVaries
ncspotTUIlibrespotNoVaries
SpotubeFlutter GUIVariesNoVaries

Figures are as documented by each project and change over time.

The community is already asking for more than a lighter Spotify. A commenter on the project's Show HN post asked whether it could ingest music from other sources.

Great work!! Would you be willing to consider supporting ingesting music using yt-dlp and libtorrent, identifying and categorizing those imports using Musicbrainz, and potentially storing them in an S3 compatible target (versus a POSIX filesystem)?

toomuchtodo, Hacker News commenter · Show HN: Fastpotify

That request is outside the project's scope today. The repository is MIT-licensed, so it does not have to stay that way.