A playlist from your own library, decided by how you feel
Describe your mood in plain words. Laya reads your songs, decides a strategy, and builds the playlist from music you already love.
Mood playlists exist, but they are built from someone else's catalog, and the signals that used to make them possible are gone.
|
Mood playlists come from a global catalog. They rarely sound like the music you actually saved. |
Since late 2024, new Spotify apps get no audio features, no recommendations and no previews from the Web API. |
People say "I'm sad but I want to feel better", not "valence 0.4, energy 0.6". |
Resonance treats it as a decision problem, not a recommendation problem. The mood of each song is read once from its lyrics and cached. Your prompt is interpreted at request time by Laya, a non-autoregressive decision model that answers typed questions with calibrated probabilities. Ranking is plain math over those cached decisions, so a new prompt takes seconds instead of re-reading your whole library.
- Library: you sign in with Spotify. Resonance reads your own and collaborative playlists and merges their tracks without duplicates. Playlists it created itself are always skipped.
- Lyrics: each track's lyrics are fetched from LRCLIB and stored in SQLite, with retries and backoff when the service is busy. This happens once per track.
- Song mood: Laya reads the full lyrics and answers two questions: which of 7 moods fits (love, happiness, comfort, sadness, loneliness, anger, fear) and whether the song is positive or negative. The full probability distribution is cached per track.
- Your mood: Laya reads your prompt and decides the signals (feels down, wants a change, wants energy, wants rest), the emotion, the situation and the strategy: keep me company, lift me up, energize or calm.
- Playlist: every song is checked against your target mood with a per-mood probability threshold, fitted on 300 user-labeled tracks (love 72%, happiness 8%, comfort 18%, sadness 14%, loneliness 16%, anger 50%; fear has no reliable threshold and instead requires the song's top mood to be fear). Qualifying songs are ordered by that probability, highest first. Lift me up builds a three-step progression, each step targeting a different mood (your detected mood, then comfort, then happiness).
Laya never picks songs directly. It interprets text into structured decisions, and those decisions choose the songs. Every step shown on screen is a real model output.
Song mood from lyrics is hard, so every design choice was tested on a labeled set before shipping.
| Measure | Resonance | Reference |
|---|---|---|
| 7-mood choice, strict (300 user-labeled) | 31% | |
| 7-mood choice, lenient (adjacent mood counts) | 55% | |
| Positive vs negative (300 user-labeled) | 45-52% | 50% (chance) |
| Playlist precision, held-out split | 48% | 32% (random selection) |
| Playlist recall, held-out split | 33% | |
| MERGE lyrics benchmark, 4 quadrants | 57.5% (macro-F1 0.57) | 0.71-0.75 macro-F1 (trained models) |
On MERGE, the 7 moods are mapped onto the same 4 circumplex quadrants used there; the reference models were trained directly on that benchmark, Laya was not.
Because the model's polarity read and single argmax mood aren't reliable enough on their own, playlist selection instead uses the full per-mood probability distribution against per-mood thresholds fitted on that 300-track set (precision-first: maximize F0.5 subject to precision beating a random baseline by at least 0.15 and recall staying at or above 0.20). Measured on a held-out split, that reaches 48% average precision against a 32% random baseline, at 33% average recall. scripts/eval_mood.py re-runs the evaluation against your own labels.
A hexagonal Python backend and a small React app. The only model in the loop is Laya, running locally on CPU.
| Component | Technology | Description |
|---|---|---|
| Backend | FastAPI with ports and adapters. SQLite caches lyrics, mood profiles and sessions. | |
| Frontend | React and TypeScript with plain CSS. Animated analysis screen and a custom player. | |
| Decision model | Laya multilingual checkpoint, batched with predict_batch. |
|
| Lyrics | LRCLIB, free and open, no API key. | |
| Spotify | OAuth with PKCE, playlist reading and saving, full-track playback via the Web Playback SDK. |
- Python 3.11+ with uv, and Node.js with npm
- A Spotify Premium account (the Web Playback SDK requires it)
- A Spotify app from the developer dashboard with this redirect URI:
http://127.0.0.1:8000/auth/callback
- Install: creates
backend/.envandfrontend/.env, then installs dependencies (uv sync,npm install). Fill inSPOTIFY_CLIENT_IDandSPOTIFY_CLIENT_SECRETinbackend/.env.The backend pulls PyTorch throughmake install
laya, so the first install takes a few minutes. Laya's weights download from Hugging Face on first use. No GPU needed. - Run: starts the API and the web app together.
make dev
- Open http://127.0.0.1:5173 and connect Spotify. Use
127.0.0.1, notlocalhost: the session cookie and the Spotify redirect are bound to it.
Create backend/playlists.local.json to limit the library to some playlists. The file is gitignored, and names match ignoring case and accents.
{
"playlist_allowlist": ["Road Trip", "Late Night", "Particula"]
}Delete the file to use every playlist you own or collaborate on.
make test # unit tests, no network, no model
make test-laya # integration tests against the real Laya modelRun make to list every command.
- Spotify sign-in with PKCE, own and collaborative playlists only
- Lyrics cache from LRCLIB with retries, backoff and resumable downloads
- Song mood profiles with a full 7-mood distribution per track
- Prompt decisions for signals, emotion, situation and strategy
- Per-mood thresholds and a lift me up progression
- Animated analysis that reveals chosen and rejected songs
- Full-track player and Save to Spotify
- Human-labeled evaluation set in Spanish and English
- Better situation detection for short prompts like "I want popcorn"
- GPU or distilled model to profile large libraries faster
- Instrumental tracks, which have no lyrics to read today
- Laya by Convai Innovations: the decision model behind every choice
- LRCLIB: open lyrics that make this possible without scraping
- Spotify Web API and Web Playback SDK: playlists, saving and playback
Important
The first run can take several minutes. Resonance downloads the lyrics of every song in your library and reads their mood with Laya on your CPU. Everything is cached in SQLite, so later runs only process new songs.
Released under the GPL-3.0 license.


