On March 1st 1973, exactly 50 years ago, The Dark Side of the Moon by Pink Floyd was released. It is hard to overstate the impact this album had on music and art in general.
For me, it is one of the key pieces of my adolescent soundscape. I remember On The Run blasting from speakers in a classmate’s car, accompanied by his verbalized doubts whether I have gone insane. The melancholic sentiment associated with the human perception of the passage of Time whenever the cacophony of the clocks starts. The unnerving confrontation with death and cesation of consciousness in The Great Gig in The Sky.
So, I decided to prepare a small tribute upon realizing the anniversary. And being the data nerd that I am, I used this opportunity to explore the data behind the album. To be more specific, the data available at Spotify. Also, I employed DALL-E to create several visual pieces based on the original artwork since I am fond of generative art. So, you may find them throughout the text along with the command prompts used in the process. Finally, you may also find trivia like breadcrumbs scattered here and there.
Enjoy!
Packaging Unlike Hipgnosis
The name of this section refers to the fact that the original artwork for the album was created by Hipgnosis, a design studio founded by Storm Thorgerson and Aubrey Powell. At the same time, it is a wordplay on the fact that the text is written using Python packages like plotly or spotipy.
At any rate, the album’s artwork depicts light refracting from a triangular dispersive prism. A spicy detail is that this physical phenomenon has found itself lost in the culture wars.
Load Python packages
import plotly.graph_objs as goimport plotly.express as pximport numpy as npimport pandas as pd# Credentials import from credentials import client_id, client_secret# Spotify connectionimport spotipyfrom spotipy.oauth2 import SpotifyClientCredentialsfrom spotipy.oauth2 import SpotifyOAuth
(a)
(b)
Figure 2: “Create an abstract, geometric image inspired by the Suprematism movement and Kazimir Malevich’s work from 1913. The image should incorporate elements that are reminiscent of Pink Floyd’s album ‘The Dark Side of the Moon’, such as a rainbow spectrum or a prism. The image should be predominantly dark in color, with shades of black, blue, and purple. The image should also include a prominent circular”
Spotify or Everything under the sun is in tune
The first step is to create a developer account at Spotify and obtain the credentials. Then, one can connect to the Spotify API using the spotipy package. There are several articles covering the process of connecting to the API, so check (e.g.) Building a CLI Spotify playlist generator using Python & Spotipy out if you want to know more.
After connecting to the API, I extracted information about Pink Floyd as artists (like 'genres'), but also about their albums (like 'release_date'). Finally, I downloaded information about individual songs (like 'popularity').
This approach allows me to focus on the album in detail, but also in comparison to other albums by Pink Floyd. But let us start with Pink Floyd as artist.
Connect to spotipy and return data on the album
# Replace the values below with your own credentialsclient_id = client_idclient_secret = client_secret# Authenticate with the Spotify API using your credentialsclient_credentials_manager = SpotifyClientCredentials(client_id, client_secret)sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)# Search for the "Dark Side of the Moon" album by Pink Floydresults = sp.search(q='artist:Pink Floyd album:"Dark Side of the Moon"', type='album')# Get the album's IDalbum_id = results['albums']['items'][0]['id']# Get the audio features for each track on the albumtracks = sp.album_tracks(album_id)['items']features = [sp.audio_features(track['id'])[0] for track in tracks]album_tracks = sp.album_tracks(album_id)['items']album_track_ids = [track['id'] for track in album_tracks]# Get the artist's IDresults = sp.search(q='artist:Pink Floyd', type='artist')artist_id = results['artists']['items'][0]['id']
Popular songs or It’s a hit!
It is no secret that Pink Floyd albums are mainly conceptual. One of the consequences are that listeners are invited to experience songs in the order suggested by the band - from the beginning to the end, as a whole. The Dark Side of the Moon is definitely in this scope.
According to the popularity of the songs by Pink Floyd on Spotify, the listeners are keen to listen multiple songs from the album. However, not necessarily in the order suggested by the band.
All in all, half of the Top 10 songs of Pink Floyd on Spotify are from The Dark Side of the Moon, namely Money, Time, Brain Damage, Breathe, and The Great Gig in the Sky.
Show the top 10 songs of Pink Floyd per albums on Spotify
# Get the top tracks for a given artistresults = sp.artist_top_tracks(artist_id)# Create a list of dictionaries, each containing track name and album nametracks = []for track in results['tracks']: tracks.append({'Track Name': track['name'], 'Album Name': track['album']['name']})# Create a dataframe from the list of track and album dictionariesdf = pd.DataFrame(tracks)# Create a dataframe with columns "Album Name" and "Count" based on the number of occurences of albums in the top 10 songsdf_count = df.groupby('Album Name').size().reset_index(name='Count') # | label: fig-albums# | fig-cap: "The top 10 albums of Pink Floyd on Spotify"# Create a bar plot of the top 10 albums# Define the color scalecolors = ['rgb(0, 0, 255)', 'rgb(255, 127, 0)', 'rgb(255, 0, 0)', 'rgb(0, 255, 255)', 'rgb(255, 0, 0)']fig = px.bar(df_count, x='Album Name', y='Count', hover_data=['Album Name', 'Count'], color='Album Name', color_discrete_sequence=colors, labels={'Count': 'Number of times in Top 10'}, height=400, template="simple_white")fig.update_traces(showlegend=False)fig.show()
Album features or All that you taste
There are several properties or audio features of the songs on the album that can be used to describe the album as a whole. For example, there is:
danceability - how suitable a track is for dancing based on a combination of musical elements including tempo, rhythm stability, beat strength, and overall regularity.
acousticness - a confidence measure from 0.0 to 1.0 of whether the track is acoustic. 1.0 represents high confidence the track is acoustic.
instrumentalness - predicts whether a track contains no vocals. “Ooh” and “aah” sounds are treated as instrumental in this context. Rap or spoken word tracks are clearly “vocal”. The closer the instrumentalness value is to 1.0, the greater likelihood the track contains no vocal content. Values above 0.5 are intended to represent instrumental tracks, but confidence is higher as the value approaches 1.0.
Let’s calculate the average of danceability, accousticness, and instrumentalness for each album and see how each of them compares to the other selected albums! But there is a disclaimer - I decided to plot only 6 selected ones out of the total of 20. The reason is that including all would - in my view - defeat the purpose. Anyway, let’s see how The Dark Side of The Moon fares!
Extract the audio features of the Pink Floyd albums’ tracks and plot them
# Get Pink Floyd's album names and IDsalbums = []results = sp.artist_albums(artist_id, album_type='album', limit=50)albums.extend(results['items'])while results['next']: results = sp.next(results) albums.extend(results['items'])# Append the album names album_names = []for album in albums: album_names.append(album['name'])# Return only distinct album namesalbum_names =list(set(album_names))# Return only studio albumsstudio_albums = []for album in album_names:if'Live'notin album and'Remix'notin album: studio_albums.append(album)album_ids = []for album in studio_albums: results = sp.search(q='artist:Pink Floyd album:'+ album, type='album') items = results['albums']['items']if items: album_ids.append(items[0]['id'])# Create a list to hold the album dataalbum_data = []# Iterate over the album IDsfor album_id in album_ids:# Get the album name album_name = sp.album(album_id)['name']# Get the track IDs for the album tracks = sp.album_tracks(album_id) track_ids = [track['id'] for track in tracks['items']]# Get the audio features for the tracks audio_features = sp.audio_features(track_ids)# Calculate the average danceability, instrumentalness, and acousticness for the album danceability_sum =0 instrumentalness_sum =0 acousticness_sum =0 num_tracks =0for track in audio_features:if track['danceability'] isnotNoneand track['instrumentalness'] isnotNoneand track['acousticness'] isnotNone: danceability_sum += track['danceability'] instrumentalness_sum += track['instrumentalness'] acousticness_sum += track['acousticness'] num_tracks +=1if num_tracks ==0: avg_danceability =0 avg_instrumentalness =0 avg_acousticness =0else: avg_danceability = danceability_sum / num_tracks avg_instrumentalness = instrumentalness_sum / num_tracks avg_acousticness = acousticness_sum / num_tracks# Add the album data to the list album_data.append({'Album': album_name, 'Danceability': avg_danceability, 'Instrumentalness': avg_instrumentalness, 'Acousticness': avg_acousticness})# Create a dataframe from the album datadf = pd.DataFrame(album_data, columns=['Album', 'Danceability', 'Instrumentalness', 'Acousticness'])# Define the regex pattern to match substrings inside parentheses or square bracketspattern =r"[\(\[].*?[\)\]]"# Use the str.replace() method to remove the substrings and trim whitespacesdf['Album'] = df['Album'].str.replace(pattern, "", regex=True).str.strip()# Create a dataframe with only the albums of interestdf_line_plot = df[df['Album'].isin(['The Dark Side Of The Moon', 'The Wall', 'Wish You Were Here', 'Animals', 'The Final Cut', 'The Division Bell'])]# Plot df using plotly express - line plotfig = px.line(df_line_plot, x=['Danceability', 'Instrumentalness', 'Acousticness'], y='Album', labels={'Album': 'Album', 'value': 'Value', 'variable': 'Property'}, title='Album features or All that you taste', template="simple_white")fig.show()
So, it seems that the Dark Side of the Moon is the most instrumental album of the selected ones. It is also perhaps a least danceable album within the selection. So, perhaps the best way how to enjoy is to warm our bones beside the fire?
But what about the popularity of the songs on the albums? And what is it in the first place? Spotify defines it as follows:
popularity - the popularity of the track. The value will be between 0 and 100, with 100 being the most popular. The popularity is calculated by algorithm and is based, in the most part, on the total number of plays the track has had and how recent those plays are.
Now, let’s see!
Extract the popularity scores of the Pink Floyd albums’ tracks and plot them
# Create an empty list to store the popularity datapopularity_data_list = []# Loop through the album IDsfor album_id in album_ids:# Get the album name album_name = sp.album(album_id)['name']# Get the track IDs for the album tracks = sp.album_tracks(album_id) track_ids = [track['id'] for track in tracks['items']]# Get the popularity values for each track popularity_values = sp.tracks(track_ids)['tracks']# Append the popularity data to the list popularity_data_list.append(pd.DataFrame([{'Album': album_name, 'Song': track['name'], 'Popularity': track['popularity']} for track in popularity_values]))# Concatenate all DataFrames in the listpopularity_data = pd.concat(popularity_data_list, ignore_index=True)# Define the regex pattern to match substrings inside parentheses or square bracketspattern =r"[\(\[].*?[\)\]]"# Use the str.replace() method to remove the substrings and trim whitespacespopularity_data['Album'] = popularity_data['Album'].str.replace(pattern, "", regex=True).str.strip()# Create a sample of 6 albums with all their songs - The Dark Side of the Moon, The Wall, Wish You Were Here, Animals, The Final Cut and The Division Belldf_box_plot = popularity_data[popularity_data['Album'].isin(['The Dark Side Of The Moon', 'The Wall', 'Wish You Were Here', 'Animals', 'The Final Cut', 'The Division Bell'])]# Keep only album and popularity columnsdf_box_plot = df_box_plot[['Album', 'Popularity', 'Song']]colors = ['blue', 'red', 'green', 'orange', 'purple', 'yellow']# Plotly boxplot on popularity_data - grouped by Albumfig = px.box(df_box_plot, x='Album', y='Popularity', title='Popularity of songs by Pink Floyd', template="simple_white", color='Song', color_discrete_sequence=colors, points="all")# Hide legendfig.update_layout(showlegend=False)fig.show()
Over all, The Dark Side of the Moon is the most popular album of the selected ones. In addition, the popularity scores of the individual songs are quite similar. This means that the album is relatively homogenous popularity-wise. Or, in other words, there seem to be no quite unpopular songs on the album.
Tribute Playlist or Different tunes
Now, Spotify API has the capacity to recommend songs based on a given song (or a list of songs). So, let’s try it out on The Dark Side of the Moon!
Create a playlist of songs that are similar to the songs on the album The Dark Side of the Moon
# Return album_id of The Dark Side of the Moonalbum_id = sp.search(q='The Dark Side of the Moon', type='album')['albums']['items'][0]['id']# Get the track IDs for the albumtracks = sp.album_tracks(album_id)track_ids = [track['id'] for track in tracks['items']]# Get song recommendations based on track_ids of The Dark Side of the Moonrecommendations = sp.recommendations(seed_tracks=[track_ids[1], track_ids[3], track_ids[5], track_ids[6], track_ids[8]], limit=50)# Create a list of dictionaries, each containing track name, artist name, album name, release date, and track uritracks = []for track in recommendations['tracks']: tracks.append({'Track Name': track['name'], 'Artist Name': track['artists'][0]['name'], 'Album Name': track['album']['name'], 'Release Date': track['album']['release_date'], 'Track URI': track['uri']})# Create a dataframe from the list of track dictionariesdf = pd.DataFrame(tracks)# Parse year from release date and create a new column "Release Year"df['Release Year'] = df['Release Date'].str[:4]# Create a dataframe with columns "Release Date" and "Count" based on the number of occurences of release datesdf_count = df.groupby('Release Year').size().reset_index(name='Count')# Create a Plotly table objecttable = go.Table( header=dict(values=list(df[['Artist Name', 'Track Name']].columns), fill_color='#18bc9c', align='left'), cells=dict(values=[df[col] for col in df[['Artist Name', 'Track Name']].columns], fill_color='whitesmoke', align='left'))# Define the layout for the table - 100% width and heightlayout = go.Layout( width=800, height=1000, autosize=True)# Combine the table and layout objects into a Figure objectfig = go.Figure(data=[table], layout=layout)# Display the table in the notebookfig.show()
Tribute Playlist
Inspired or inspiring? Or both?
The generated list of songs is quite interesting. It contains songs across decades and genres. There are songs from the albums preceeding as well as following The Dark Side of the Moon.
Some might consider thinking about this as an “inspirational lineage”. So, let’s take a look at the number of songs from the list released per year.
What was the year of release of the recommended albums?
# Create plotly bar chart of the number of songs released per year. Add color one bar.fig = px.bar(df_count, x='Release Year', y='Count', title='Number of songs released per year', template="simple_white", color_discrete_sequence=['blue', 'red', 'green', 'orange', 'purple', 'yellow'], color='Release Year')# Update the layoutfig.update_layout( title_text='Number of songs released per year', xaxis_title="Release Year", yaxis_title="Number of Songs", xaxis_tickangle=-45, xaxis_tickfont_size=14, yaxis=dict( tickmode='linear', tick0=0, dtick=5 ), bargap=0.15, bargroupgap=0.1, template="simple_white")# Hide legendfig.update_layout(showlegend=False)# Display the bar chart in the notebookfig.show()
All right, Spotify recommends quite evenly across decades, but the distribution of values is denser towards the 70s.
Another neat capacity of the API is the option to create a playlist programmatically. Let’s try it out! A small tip - in case you want to run your own “app”, don’t forget to set up the redirect_uri in the Spotify Developer Dashboard.
Programaticaly create a playlist named See you on The Dark Side of the Moon
# Create a Spotify playlist based on the recommendations# Get the client ID and client secret from the environment variablessp_oauth = SpotifyOAuth( client_id=client_id, client_secret=client_secret, redirect_uri="http://localhost:8000/callback", scope="playlist-modify-public")# Token generation: # access_token = sp_oauth.get_access_token()access_token = sp_oauth.get_cached_token()sp = spotipy.Spotify(auth=access_token["access_token"])# Get the user's Spotify usernameusername = sp.current_user()['id']# Create a new playlistplaylist = sp.user_playlist_create(username, 'See you on The Dark Side of the Moon')# Get the playlist IDplaylist_id = playlist['id']# Get the track URIstrack_uris = df['Track URI'].tolist()# Add the tracks to the playlistsp.user_playlist_add_tracks(username, playlist_id, track_uris)# Add the description of the playlistsp.user_playlist_change_details(username, playlist_id, description='A playlist of songs that sound like "Time" by Pink Floyd')
So, the playlist should be ready. Let’s check it out here.
Honorary mentions or All you create
Before I wrap up this post, I would like to turn your attention to some of The Dark Side of the Moon-related projects.
First of all, Hans Zimmer used Eclipse in the trailer for Dune. It is a nice touch on the megalomaniac project by Alejandro Jodorowsky as Pink Floyd was supposedly on his short-list for the movie soundtrack. Full circle.
Secondly, if you like rock music, don’t miss out on The Doom Side of The Moon as it is a rewarding take on the album from a more metal-ish perspective.
Finaly, Darkside or Tom Stoppard’s excellent mashup of the album and topics such as the trolley problem, the tragedy of the commons, and the prisoner’s dilemma. Remember Iwan Rheon as Ramsay Bolton in Game of Thrones? He is one of the characters and let’s just say that the wheels are not turning well for him.
Closing words or There is no dark side in the moon, really
Great, that’s it. If you have made it this far, I bow to you and thank you for reading! I hope you enjoyed the post. If you have any questions or comments, please feel free to reach out to me via Contact.
(a)
(b)
Figure 3: “Pink Floyd, The Dark Side of the Moon, abstract, full hd render + 3d octane render +4k UHD + immense detail + dramatic lighting + well lit + black, purple, blue, pink, cerulean, teal, metallic colours, + fine details + octane render + 8k”