Why This Song?
How It Works
This app uses content-based filtering to recommend songs based on their musical features. Each song is represented as a point in a 4-dimensional space using:
- Tempo: Speed in BPM, normalized to 0-1 (dividing by 200)
- Energy: Intensity and activity level (0-1)
- Danceability: How suitable for dancing (0-1)
- Valence: Musical positivity/happiness (0-1)
The algorithm follows three steps:
- Step 1: Learn your preferences - Learns from all your ratings: 3★ is neutral, >3★ pulls toward a song's features, <3★ pushes away. Tempo is normalized by /200.
- Step 2: Score candidates - Calculates similarity between your preferences and each unrated song using weighted features
- Step 3: Recommend best match - Picks the highest-scoring song, with a genre bonus if it matches your favorite genre
Weights control how important each feature is
(always sum to 1). Genre bonus adds extra points
when the song's genre matches your most-rated genre. You can
toggle between abs-diff (absolute difference) and
cosine similarity methods.
Workshop Guide: What to Do
Your mission: teach the recommender what you like by rating songs, then experiment with the controls to see how the algorithm changes what it recommends.
Step-by-step
- Rate songs with 1–5 stars. No skipping—every rating teaches the model.
- After a few ratings, read Why This Song? and the Feature Comparison bars to understand the match.
- Tune the weights: drag one feature high and others low. What kinds of songs appear? Then reset weights and try a different emphasis.
- Switch similarity (Abs-Diff vs Cosine). Does the recommended song change? Why might these two methods disagree?
- Toggle the Genre Bonus. Does your top genre influence the pick? Predict first, then check the result.
- Keep rating until you notice a pattern in your preferences (energy, tempo, danceability, valence).
Challenges
- 90% Club: Achieve a ≥90% match on a song you genuinely like. What settings helped you get there?
- 5th Dimension: Reduce the dataset to a few songs only. Add a 5th dimension to the songs such as "loudness" in [0,1]. Update the similarity calculations to include this new feature, and see how it affects recommendations. Hint you'll need to update computeUserPreferences(), scoreSong(), and renderBars() to handle the new feature.
- Moods are Clusters: Similar music tends to group into clusters (e.g., high-energy dance tracks vs. low-energy ballads). Can you add the ability to play music for a certain mood by identifying clusters in the feature space? For example, you could play a workout playlist or one for focusing.
The Math Behind Similarity
We compare your preferences
p = [tempoN, energy, danceability, valence] with a
song's features s using one of two functions (current mode:
abs-diff):
-
Abs-Diff (weighted absolute difference):
score = Σ w_i · (1 − |p_i − s_i|)
Each feature is in [0,1] (tempo is normalized by /200). The best possible per-feature term is 1 when they match exactly. -
Cosine (weighted cosine similarity):
Scale each component by√w_i, then computesim = (p·s) / (||p||·||s||), wherepandsare the scaled vectors.
We convert this into a score in [0,1].
Genre bonus: If a song's genre matches your
most-rated genre, we add a bonus:
score += genreBonus.
Match %:
match = round(100 · score / (Σw + bonus_if_applicable)), clamped to [0,100]. Here Σw is the sum of feature
weights (1 by default), and the denominator includes the genre
bonus only when it applies.