Skip to contents

This vignette walks through a complete analysis of a single song — from fetching lyrics to plotting sentiment, emotions, complexity, and themes.

Setup

Set your Genius API token before starting. You can get one at https://genius.com/api-clients.

Sys.setenv(GENIUS_API_TOKEN = "your_token_here")
library(lyricsLab)

Fetching lyrics

get_lyrics() searches Genius for the song, scrapes the lyrics page, and returns a lyrics_lab object containing the lyrics, metadata, and detected song structure.

lyrics <- get_lyrics("Billie Eilish", "bad guy")

Use preview_lyrics() to check what was retrieved:

preview_lyrics(lyrics)
#> Song:     bad guy
#> Artist:   Billie Eilish
#> Album:    WHEN WE ALL FALL ASLEEP, WHERE DO WE GO?
#> Lines:    47
#> URL:      https://genius.com/...
#> Sections: verse, chorus, outro
#>
#> duh
#> I'm the bad guy
#> duh
#> I'm the bad guy
#> ...

Use view_lyrics() to print everything:

view_lyrics(lyrics)

Sentiment

Overall score

sentiment_score() computes a single score for the whole song using the AFINN lexicon (or bing/nrc if you prefer). It prints a summary and returns results invisibly.

result <- sentiment_score(lyrics)
#>
#> === Sentiment: bad guy by Billie Eilish ===
#>   Method:          afinn
#>   Score:           -0.34
#>   Normalized:      46.6 / 100
#>   Interpretation:  Neutral - balanced emotional tone
#>   Positive lines:  19.1%
#>   Negative lines:  25.5%

Switch methods with the method argument:

sentiment_score(lyrics, method = "bing")
sentiment_score(lyrics, method = "nrc")

Sentiment arc

sentiment_arc() scores every line individually and describes how sentiment shifts through the song:

arc <- sentiment_arc(lyrics)
#>
#> === Sentiment Arc: bad guy by Billie Eilish ===
#>   Method:          afinn
#>   Mean sentiment:  -0.34
#>   Trajectory:      Maintains consistent tone
#>   Positive lines:  9
#>   Negative lines:  12
#>   Neutral lines:   26

Visualize the arc with plot_sentiment_arc():

Each point is one lyric line, coloured green (positive) through red (negative). The smoothing line shows the overall trajectory. Turn it off with smooth = FALSE:

plot_sentiment_arc(lyrics, smooth = FALSE)

Positive/negative ratio

positive_negative_ratio(lyrics)
#>
#> === Positive/Negative Ratio: bad guy by Billie Eilish ===
#>   Ratio:           0.75:1 (positive:negative)
#>   Interpretation:  Balanced
#>   Positive lines:  9  (19.1%)
#>   Negative lines:  12 (25.5%)
#>   Neutral lines:   26 (55.3%)

Emotions

analyze_emotions() uses the NRC lexicon to detect eight emotions: anger, anticipation, disgust, fear, joy, sadness, surprise, and trust.

analyze_emotions(lyrics)
#>
#> === Emotions: bad guy by Billie Eilish ===
#>   anticipation   3 words (0.51%)
#>   joy            2 words (0.34%)
#>   trust          2 words (0.34%)
#>   anger          1 words (0.17%)
#>   ...
#>   Dominant emotion: anticipation

Two plot options — bar chart or radar/web chart:

The radar chart is useful for comparing the emotional “shape” of songs side by side visually.

Complexity

complexity_score() measures vocabulary richness, average word length, syllables per word, and estimates a Flesch-Kincaid reading level, combining them into a single 0–100 complexity score.

complexity_score(lyrics)
#>
#> === Complexity: bad guy by Billie Eilish ===
#>   Vocabulary size:     106 unique / 210 total words
#>   Vocabulary richness: 0.505
#>   Avg word length:     3.81 characters
#>   Syllables per word:  1.29
#>   Reading level:       3.2 (grade)
#>   Complexity score:    24.6 / 100
#>   Interpretation:      Elementary level - very simple vocabulary

A high type-token ratio (unique/total) indicates more varied vocabulary. Reading level is estimated grade level — elementary through graduate.

Themes

theme_detection() matches lyrics against keyword lists across 23 theme categories and shows the top matches:

theme_detection(lyrics)
#>
#> === Themes: bad guy by Billie Eilish ===
#>   Lust/Desire            4 matches (22.2%)
#>   Jealousy               3 matches (16.7%)
#>   Rebellion              3 matches (16.7%)
#>   Empowerment            3 matches (16.7%)
#>   Time/Change            2 matches (11.1%)
#>
#>   Dominant theme: Lust/Desire

Plot the theme breakdown:

plot_themes(lyrics)

Storing results

All analysis functions print to the console and return results invisibly. Capture them by assigning to a variable:

sent   <- sentiment_score(lyrics)
comp   <- complexity_score(lyrics)
themes <- theme_detection(lyrics)
emo    <- analyze_emotions(lyrics)