Skip to content

Features

Native UI and view modes

Spooty's UI is native Jetpack Compose: Home, Search, Library, Marketplace and Profile. The Spotify web player runs hidden behind it and is only shown when you ask for it:

  • Native (default): the Compose app, with the web player handling login and audio.
  • Web view: the real web player, reshaped for a phone. Spotify's top bar stays, the sidebars fold away (the library opens from a button), and a docked player card holds the cover, seek bar, controls, sleep timer, picture-in-picture, lyrics and queue. The card's tools also open Spooty's settings and switch back to the native view.

App styles change the whole native UI: Default, Liquid Glass (a floating glass tab bar and mini player that refract what scrolls under them, Android 13+), Glassmorph (frosted panels over deep ink and pools of light, one taking the album's colour) and Frutiger Aero (a glossy mid-2000s sky with an aurora, a green hill and bubbles).

Player look

The full player's look sheet has live previews drawn from the current cover:

  • Album art: cover, vinyl, or art surrounded by a visualizer (ring, pulse, liquid, sparks).
  • Visualizer: bars, wave, spectrum or LED under the art, tinted with the album colour. When audio plays natively (YouTube, downloads, local files) they follow a real FFT of the sound; Spotify's own stream is DRM-locked, so they move to the beat instead.
  • Seek bar: default, Nyan Cat, or any Spicetify Marketplace snippet that decorates the playback bar (Sonic, Pokémon, Kirby, Oneko, hamsters, Minecraft…). Snippet CSS is translated into native sprites, and also applies in web view mode.
  • Dancing buddy: Cat Jam, nodding at the song's detected tempo.

Spicetify Marketplace

The Marketplace tab reads the same sources as the Spicetify Marketplace: GitHub repos tagged spicetify-themes / spicetify-extensions, each repo's manifest.json, the Marketplace's snippets.json, and its blacklist. Catalogues are cached for a day.

  • Every entry has a preview image and a page with its README rendered as Markdown.
  • Themes: color.ini restyles the native app; user.css and theme scripts style the web view.
  • Extensions run in the page on Spooty's Spicetify layer. Spooty reads which Spicetify.* APIs an extension calls and labels it Works, Works (buttons show in web view) or May only partly work, naming what's missing.
  • Snippets are CSS for the web view; playback-bar snippets can also be used on the native seek bar.
  • Custom apps need the desktop Spicetify app and aren't supported.

Song extras

The ⋮ menu (and a row of buttons in the full player) offers:

  • Like, Add to playlist, Play next / Add to queue, Download.
  • Song radio: Spotify's "inspired by" playlist for the song.
  • Credits: performers, writers and producers.
  • Music video: Spotify's own video when the song has one (played by the web player and shown full screen), otherwise the official video from YouTube.
  • Share, a share card with a QR code, and Copy link.

Artist pages show an On tour section with upcoming concerts.

Spooty DJ

A free take on Spotify's DJ, like a personal radio station. Start it from Home: it builds a set around what you're playing (Spotify's song radio, or your own home mixes when radio isn't available) and talks between songs: it roasts your taste a little, drops a fact about the artist and hypes the next track, while the music dips under it.

  • Who writes the lines (Settings → Spooty DJ): Automatic uses your own free Gemini or Groq key when you add one, otherwise Pollinations (free, no key), otherwise built-in lines. Each line is written while the previous song plays. The AI only receives song names and the artists you play most; Built-in lines sends nothing.
  • Voice: Microsoft Edge's free neural voices (Andrew, Ava, Brian, Emma, Ryan, Sonia), or the phone's own text-to-speech. If the online voice is unreachable the phone's voice takes over, so the DJ never goes silent.
  • The Home card can be hidden with its ✕ and brought back in Settings.

The Edge voice token code is a port of edge-tts and is the one file in Spooty licensed under the LGPLv3.

Browse

With the search box empty, Search shows Spotify's Browse tiles (genres, moods, charts) in their own colours. Each opens a page of playlists, albums and sub-genres.

Queue

Play next and Add to queue work whatever is playing, including an album or playlist that Spotify's web player is playing. Album and playlist pages can queue every song at once, next to the download-all button.

Notification and lock screen

The media notification shows the cover, title, artist and album with a seek bar, previous / play-pause / next, plus Like and Shuffle buttons.

Profile

Your profile shows followers, following and playlists (each opens a list), liked songs, your playlists (with See all and a search box), recent artists, friend activity and new releases from artists you follow. Log out lives at the end of Settings, behind a confirmation.

Playback sources (audio mode)

The audio backend is configurable in settings and defaults to Spotify first:

Mode Behaviour
SPOTIFY_FIRST (default) hands the URI to the hidden WebView immediately (authentic Spotify audio); if the web player hasn't started within 12 s, falls back to a native YouTube stream via ExoPlayer, with a safety re-arm if that also stalls
SPOTIFY always play through the hidden WebView
YOUTUBE always resolve a native YouTube stream, falling back to the WebView on failure
AUTO prefer the native YouTube stream, fall back to the WebView on failure

The web player's encrypted Spotify audio is decrypted by the WebView's own Chromium, so no native DRM re-implementation is needed.

Honest login (token gate)

The session token is trusted only when the account's sp_dc cookie is present in the WebView (and /v1/me answers 200 or a cookie-backed 429). Anonymous or rate-limited sessions are never silently accepted, so:

  • the app stays truthfully logged out until a real session exists,
  • Continue / Log in always routes through accounts.spotify.com,
  • a manual sp_dc cookie import (Settings) is available as a fallback login.

Ad handling — quiet by default

The ad engine is a pure-JS layer injected into the page. It does no network interception and no packet-level filtering — it observes the player and reacts.

  • Mute is the default. When an ad starts, the engine mutes the WebView's audio (recording the previous volume so it can restore it), and unmutes when the ad ends.
  • Skip is opt-in. An explicit toggle in settings arms skip:

    • only when the player reports an ad (contentType === "ad" or a spotify:ad: URI),
    • only if the "track" is under 90 seconds,
    • with a 5-second cell cooldown per ad,
    • rate-limited to 3 skips per 10 minutes.
  • Two detection layers. The engine scans the webpack module registry (ListPlayer) for a clean playback-state signal, and falls back to DOM probing ([aria-label~="Advertisement"]) when the registry isn't reachable.

  • Modes are runtime-switchable (off / mute / mute+skip) from the when ad settings screen.

Themes

Spicetify-style theming, fully offline:

  • themes/win95, themes/spotify-dark, themes/retro-player — each a color.ini palette plus a user.css.
  • The theme runs through the Theming skill pipeline: palette → CSS custom properties → injected stylesheet, applied and animated back out live.
  • Switch themes any time from settings without reloading the player state.

Dynamic User-Agent Login Flow

Spooty seamlessly flips the User-Agent during navigation to ensure a smooth authentication experience: - When navigating to accounts.spotify.com, Spooty switches to a mobile User-Agent. This ensures the login screen renders perfectly for mobile users, avoiding the broken desktop login view. - Once authenticated and redirected back to open.spotify.com, the User-Agent is immediately flipped back to the desktop version to serve the full Spotify Web Player experience.

First-Launch Customization Sequence

To provide a personalized onboarding experience: - On the first launch, users are greeted with an interactive Onboarding screen (OnboardingActivity). - It features pulsating animations and allows users to set their preferences immediately, such as the global UI corner radius using a slider. - These settings are saved in the SettingsStore and immediately applied across the entire app.

Native settings screen

A programmatic UIKit settings screen with a single source of truth (SettingsStore on DataStore preferences):

Setting Effect
Ad mode off / mute / mute + skip
Theme active bundled theme
Background playback starts/stops the foreground MediaSession service
Desktop user-agent reloads the player with desktop UA
DNS preconnect warms Spotify origin DNS at app start
Renderer prewarm constructs an early WebView to warm the renderer process

Every change is wired end-to-end: the settings button inside the app (injected by core-spooty.js) calls SpootyBridge.openSettings() → SettingsActivity.

Background playback & lockscreen controls

  • The foreground SpotifyPlaybackService mirrors track/title/play-pause state to a MediaSession and keeps the process alive for background audio.
  • Foregrounding is deterministic (FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK), so background audio keeps playing and the lockscreen shows the artwork/track metadata without the app needing to be on screen.
  • Native ExoPlayer is the real player for YouTube-proxied audio (with a Range/UA-fixing datasource for 403-gated hosts); the WebView plays the Spotify stream itself.

Connectivity-aware caching

A NetworkMonitor flips the WebView's cacheMode:

  • online → LOAD_DEFAULT
  • offline → LOAD_CACHE_ELSE_NETWORK

so the app keeps working (as much as the web player will allow) when you lose the network.

Performance & battery

  • Startup prewarm: DNS preconnect to Spotify's origins and renderer warm-up, both feature-guarded via settings.
  • No unbounded wake locks: the foreground mediaPlayback service covers audio; there's no manual PARTIAL_WAKE_LOCK.
  • Proper WebView teardown (removeView → destroy) to avoid native memory leaks.
  • No WorkManager abuse: foreground services only for what must run now.