Visualizing a Disruptive Album: beerbongs & bentleys

I recently read Fullstack D3 and wanted to put some of what I learned into action. If you haven't read it yet and want to learn D3, I'd skip this post and go order it. Seriously, it's so good.

Heads-up: Bear clocks this at around a 13-minute read, so you may want some tasty beverage if you're going to brave it.

Backstory #

A couple of years ago, I got into tracking Spotify Charts. At the time, I wanted to learn vanilla JS, so I built a little single-page app using Firebase. It's not online, but the repo exists. Each day, I'd plot the top ten tracks and then use line charts to show their performance over time. In the middle of my daily tracking, I saw something crazy. Ed Sheeran dropped ÷, and all of a sudden, there was a sea of that blue album cover in the charts. Of the sixteen tracks on the album, ten made the top twenty. I was in awe. How amazing is it that an album can drop and that all over the world, people are listening to it more than almost anything else?

Fri-yay Traditions #

Post Malone has dropped two albums with this kind of swagger, and one has become one of my favorite albums. My friend, Eden, and I sync Spotify over Discord and listen to beerbongs & bentleys every Friday afternoon. I want to visualize some of the metrics that show how strong that release was.

Selling Points #

There are a few things that stand out to show the impact that this album had on the charts: total streams on day one, that week's total streams in the top twenty vs. the previous and next week, how many tracks were in the top twenty, and how long they stayed.

On a more granular level, there are some interesting things that we can learn from the performance of individual tracks. One funny thing is that Jonestown (Interlude) is a 1:52 minute interlude that was the 30th most played song in the world on the release date. That's just cray. Aside from that, it's interesting to see how track number affects the performance and how the singles performed vs. the new tracks.

My New, Shiny Hammer #

Fresh off of reading a book about D3, I want to make sure that I'm not falling into the scenario where I have a new hammer, and everything looks like a nail. I had to ask myself if I need D3 to present this information. The answer was a mix of yes and no. There is some information that can be presented as plain text but will benefit from visualization. Client-side JavaScript is expensive, so I like to avoid it when I can. That said, I'm not building a graph out HTML and CSS. So, I'll start by comparing some of the information.

Blame It On These #

Streams #

Date Total Streams Notes
April 20, 2018 86,168,208 Release day streams one week before
April 27, 2018 91,695,538 The album had 77,452,211 of these streams, or 84%
May 4, 2018 67,147,820 Release day streams one week after

This is where I start spinning the hammer in my hand. That little table technically tells the story, and it might be exactly what you want if you were looking to copy and paste from Wikipedia into an essay. As a fanboy, though, it needs some Candy Paint.

In a surprising twist after making some pretty amazing visualizations, creating a bar chart wasn't easy. The main thing I did wrong was assuming that I could do it without reading docs. Creating a regular bar graph instead of a histogram requires you to use some different d3 methods. I had used them before, but it was an opportunity to take a break from the book and dive into converting some examples to The Watterberger style.

Disclaimer: I'm sharing very visually simple versions in this post. The book teaches you to make some very beautiful designs, but I'm going to make some questionable decisions with these as I try to bring the spirit of Post Malone to them. I don't want you to get the impression that I learned them from the book, so, for now, I'll use some very basic styles. I have to say, though, there's something really nice about these greyscale ones. I dig em.

The First Chart #

The book covers histograms, but not basic bar charts, so I applied the basics of what I had learned in this and other spots. In this one, I'm aiming to visualize that beerbongs & bentleys increased the overall streams of the top 20 by over 4 million streams compared to the surrounding weeks. If this was complex data, I'd have used a Y-Axis, but I think that a bar with an abbreviated value says more.

Total Streams of the Spotify Top 20

86.1M91.6M67.1MApril 20, 2018April 27, 2018May 4, 2018

Sample on CodePen

WARNING: This album is not safe for work and I'm sharing the actual data. If you should not have profanity and drug references on your screen or are offended by them, I'd suggest closing this tab.

Leveling Up #

Looking over some items on Observable led me to this horizontal bar chart, which seemed like the perfect way to show how all of the tracks from the album placed on the release day. This was tricky, in that I had to invert the scale. By default, if a track was 1st it would be a small bar, and if it was 24th it would be a large one. For this to work, that needed to be the opposite. Luckily I had done this in the past, but it was tricky to get it working in this new style that I'm getting used to.

Also, if I was true to the scale, I'd have shown items from 1 to 30. However, that would mean that the track that was 30th wouldn't have any height. I increased the scale to the expand by two so that it would give that item some space on the chart.

Track Positions on Release Day

ParanoidSpoil My Night (feat. Swae Lee)Rich & SadZack And CodeineTakin' Shotsrockstar (feat. 21 Savage)Over NowPsycho (feat. Ty Dolla $ign)Better NowBall For Me (feat. Nicki Minaj)OthersideStayBlame It On MeSame Bitches (feat. G-Eazy & YG)Jonestown (Interlude)92 ExplorerCandy PaintSugar Wraith246101371113121915221630212427

Sample on CodePen

A limited dataset, but... #

I'm not sharing enough of the data yet, but one takeaway from researching the charts is that in the age of the playlist, many people still listen to full albums. One thing I wonder is if people listen straight through, or shuffle, and what impact that has on chart performance. I'm sure people at Spotify know, but it's a chance to plot this basic example in a chart that I learned in the course.

Ideally, this would show the results from a bunch of albums to get a real answer. However, this gives you an idea of the relationship between track number and chart position on release day, when the album was fresh to listeners.

I wasn't sure whether or not to invert the ranges for this one. It felt right to look from the bottom and go upwards and to the right.

024681012141618202224262830Track Position0123456789101112131415161718Track Number

Sample on CodePen

Generally, the higher the track, the further down the charts it placed. The outliers were tracks 8 and 9, Psycho and Better Now. Psycho was a single that was released before the album and was #5 globally on the day before the release. Better Now turned out to be the runaway hit of the album, achieving #1 for weeks.

Spoil My Life #

This is where my obsession with unsustainable data comes in. This data isn't available in the API, and I haven't learned enough web scraping skills to automate grabbing and formatting it in a way I need. So, I made a google sheet. It's painstaking work, but I recently did step back and find a way to automate part of it. I use JS to only show the artist results on the page, which minimizes the results. If you wanna see it in action, go to the launch day chart or any day and pop this in the console, changing the artist as you see fit.

const artist = "Post Malone";
const size = 200;
const tableRows = document.querySelectorAll(".chart-table tbody tr");
const rows = Array.from(tableRows);

const sliced = rows.slice(0, size).map(slice => {
if (!slice.innerHTML.includes(artist)) {
const position = slice.querySelector(".chart-table-position");
const title = slice.querySelector(".chart-table-track strong");
const track = slice.querySelector(".chart-table-track span");
slice.parentNode.removeChild(slice);
}
});

If anyone from Spotify happens to see this, hook a front-ender up with a Charts API! I'm not expecting that anytime soon, though, so learning Python is on my shortlist.

Most of your Top 20 are belong to Post Malone #

On the day before the release, there were two singles from the album in the Spotify charts, but only Psycho was in the top 20 at number 8. It took until June 19 for it to get back down to one, which was then Better Now at number 8. The interesting part is that it was another disruptivish album that knocked Psycho back down to 27th: ? by XXXTENTACTION. That album had six tracks in the top 20, but it also revived his last album, and three tracks from 17 were now in the top 20 too. I don't remember that release, but the weird part is that this was a Tuesday, like the old school music release days.

I debated a lot between a bar chart and a line chart for this, but I went with the bars because the goal is to show the density that the album had. A line chart with an area fill worked too, but I had to round the values to make the lines not zigzag, which reduced the accuracy. This felt liked it worked.

I let d3 generate the ticks automatically by passing the dates as the range. If I was manually setting them, they'd be two days beforehand, but the cool thing is that you get to see the count two days after each week's New Music Friday. For this album, you can see that general time had more of in impact than other album releases.

Apr 29May 06May 13May 20May 27Jun 03Jun 10Jun 1702468101214161820

Sample on CodePen

This doesn't have the "wow factor" that I had expected. It's pretty amazing to drop an album and have 13 of the tracks in the top 20 songs being played in the whole world (on Spotify). The graph doesn't do it justice, buttttt just for fun; let's look at the most recent album drop by Bieber. This is only a week of data, but his had 1 before release, 3 on the release day, and 2 eight days later.

Thu 13Fri 14Sat 15Feb 16Mon 17Tue 18Wed 19Thu 20Fri 2102468101214161820

18 Track Explorer #

The last bit to present is the performance of individual songs. This one is a lot of data. So, I'll start with two individual tracks and then lead into the whammo with the 13 tracks that were in the top 20.

In trying to move the data out, I realized that I had missed the mark on organizing it in a way that I could just use it. This is a popular problem in visualizations and I could learn some manipulation techniques that would let me get what I need programmatically. Since it's a limited set and I built it, I manually made some changes to get what I need. As an aside, I'm so thankful for the many tools like CSV2JSON that exist on the web.

A lightbulb moment #

In setting up the first track for a line graph, I knew I was going to have to fight the flipping of the range again. I was debating cheating and setting the values to negatives. Well, debating isn't accurate. I did it, and I didn't like that I'd then have to fight the labeling, output of the values, etc. I tried using a custom range and 💥! In this one, I set the range from 20 to 0, and the chart instantly works. There's one additional step that I'll have to learn how to programmatically counter in the future, but I found a fix for this example.

20181614121086420RankApr 29May 06May 13May 20May 27Jun 03Jun 10Jun 17

If I wanted to only show the lifeline of the track itself, there would be no issue. Dates without a top 20 ranking would be reduced from the array. However, I want to show the lifeline of the track throughout the album's top 20 date range. Dates with no value end up creating this weird extra line. My fix, for now, was to force a value outside of 0-20 on blank positions. I did this manually, so I'll have to learn how to handle this programmatically in the future.

Assigning a value of 30 to all of the dates with an empty position fixes the stray line issue and allows you to see how Paranoid performed throughout the time that beerbongs & bentleys was disrupting the top 20.

20181614121086420RankApr 29May 06May 13May 20May 27Jun 03Jun 10Jun 17

Sample on CodePen

It opened and peaked at No. 2, and its last day in the top 20 was May 17, 2018. That makes for a nice 20-day run in the Spotify Top 20, which is a funny coincidence for a geek like me.

Misdirection #

I've been spewing text into this post for a while, showing all of the cool things that I've been able to build with d3, but there's a twist: there's no d3 on this page. I knew that this post was going to be heavy and it felt like drawing multiple graphs and including images and tons of text might not perform well. So, I tested copying an SVG out of the DOM in CodePen, where I've been building all of the examples. It worked as expected! So, all of the graphs above are just SVG code.

The most important part of that, though, is the trade-off. The power of d3 comes in the interactivity, which you lose with pure SVG. Yes, you can add animations back in, but the work that would be required to provide tooltip data would be extreme. So, only do this for pure charts that have no interactions. (And don't make too many of those.)

Just to be sure I'm not understating the value of having d3 on the page, I was able to chart out the performance of 13 individual tracks, using essentially the same code. This is all it took to repeat the line chart in the full viz. I'm assigning a an element id to target and the data. I could make that even more clean by looping over them, but honestly the code for the full vizualization is a dumpster fire.

Aside: I've learned that when applying something newly learned and designing in the browser, something has to be a trade-off. For me, that was code cleanliness in this project. This is not a humble brag, where you go peek at GitHub and I have a stray indent or something. It's a dumpster fire, but it's so much fun.

drawLineChart("paranoid", paranoid.rankings);
drawLineChart("spoil-my-night", spoilMyNight.rankings);
drawLineChart("rich-and-sad", richAndSad.rankings);
drawLineChart("zack-and-codeine", zackAndCodeine.rankings);
drawLineChart("takin-shots", takinShots.rankings);
drawLineChart("rockstar", rockstar.rankings);
drawLineChart("over-now", overNow.rankings);
drawLineChart("psycho", psycho.rankings);
drawLineChart("better-now", betterNow.rankings);
drawLineChart("ball-for-me", ballForMe.rankings);
drawLineChart("otherside", otherside.rankings);
drawLineChart("stay", stay.rankings);
drawLineChart("same-bitches", sameBitches.rankings);

Better Now #

I learned a ton from the book and from creating the items that I needed to write this post. I've shared the basics of what I learned from Amelia Wattenberger, in Fullstack D3 and Data Visualization, but the visualizations here have been monotone and lacking interactivity. The examples of the book are significantly better than what I've created here. This CodePen collection will show you most of what you will learn. I left off examples that were tricky to put into a pen.

I'm going to go a little cray in seeing how I can make something more "art" than science. While I hope that the end result delivers the information properly, it's going to be with some flare. As Post says...

Candy paint with the white on top
Lambo doors of the oo-op drop

Honestly, I don't even know what that means, but I like it. Check out the viz, Disruptive Album: beerbongs and bentleys, if you're into that kind of thing.

← Home