Gemini: The aesthetic now playing app for Spotify

September 2020
"Gemini: The aesthetic now playing app for Spotify" cover image

The app that seemed so simple, but challenging.

I thought I would never get this far coding an Electron app and literally typing lines of code. I'm happy on the outcome of Gemini, and lots of other people are too, like over a hundred people on GitHub starred it. It was a wild adventure to make this, even though the app's concept seems so simple.

Gemini is an aesthetic "now playing" player for Spotify. It shows the title and artist of the song with the album art filling the background of the screen. I was trying to make an app that you can put in like a boba shop, so people know what song is playing—in an aesthetic way.

The beginning title wasn’t actually “Gemini,” it was called “spotify-now” at first. It took me a day to come up with this. It just strictly showed the title, artist, and album art of the currently playing song. It didn’t even do anything else, literally. It didn’t change when the song changed, and you had to manually put in a Spotify API token in the code every time you wanted to use it. I even looked back at my first stack overflow question, and I really look like a noob now looking back. Going through the Spotify Oauth2 was a pain—even manually at the time for me. I was too lazy to make it automated, so the project was left in the dust for a month.

That was until, my friend Gabe came in. He wanted a Spotify mini player for his desktop. At first, he tried editing the CSS of the Spotify Web Player so that only the bottom bar of the app shows when resized. It worked, but that wasn’t a very good solution. Then he tried making a Python app, which worked for him, but when he wanted me to try it out, I couldn’t get it to install due to all the Python dependencies messing up.

Gabe eventually picked up on “spotify-now” and automated the whole authentication process. Honestly, he was the one that sparked this project again. We ended up improving it until we got something to release. But during that time, it was like we were making our first baby steps. We always would accidentally push our Spotify client ID and secret because we didn’t know about environmental variables. But finally on August 27, 2020, we released Gemini v3.1.1 on GitHub, and posted it on the r/spotify and r/node subreddits. After that, we went to sleep.

When we woke up, we saw that it was gaining traction on Reddit and was up about thirty stars on GitHub. We were surprised. Lots of amazing comments and suggestions pushed us to do more.

We eventually released v4.0.0 about a month later, but it was a huge adventure to get there.

First, we added an overlay to control Spotify, with just your simple multimedia controls (except volume because we didn’t figure out an efficient way to make it). Then we made the window frameless.

During the development process, I added an easter egg where if you play a specific song, it would show the Spotify Canvas video—the video visualizer that plays in the mobile app if the song has it. Unfortunately, Spotify doesn’t have an API to get those videos, so we had to screen record the videos off the app itself. It was only for one song, so it wasn’t a huge problem to deal with.

When I was also developing, I saw that Electron kept giving me a security warning about worldSafeExecuteJavaScript. I wanted to make sure I cleared all the errors before v4, so I started researching it. At first, I was confused about using Electron’s Inter-Process Communication. But then I realized, this lets us talk to the main process through the renderer process and vice versa. Before, we just made a local web server and made REST API requests through the main and render process. So hey, killing two birds with one stone—heighten security and eliminate the need for a web server.

Remember that easter egg I was talking about? Ironically, the most simple thing you would think would be the easiest to code was actually the hardest. We started to put more videos in, so we made an online JavaScript file that gives back a set of Spotify song IDs that have video URLs attached to them. This was a really bad decision, as someone could inject code into the online JavaScript file if my server got hacked, and if my server was down, the whole app would cripple since it literally required the file. So I had the idea of using mongoDB.

Once I got mongoDB working, I probably ran into the biggest obstacle I’ve ever encountered in this app, and that’s why I remember it so much. I needed to find a way to control when the album art fades in or not. Normally, our app doesn’t fade out the album art if the next song is in the same album, but we needed to somehow incorporate the videos aspect of it. It was really confusing to wrap our heads around it, but then I found a good way to visualize it. I recently started college, and one of the classes I’m taking is discrete mathematics. From my knowledge of that class so far, I used a truth table to visualize it. A truth table basically shows whether a proposition is true, based on two booleans. It’s kinda hard to explain, and I’m definitely not certified to explain it anyway. But if you take a look at my notes below, you can see how the code and the truth table diagram on the bottom left connect:

My notes on when I wanted the album art to change. There's a diagram showing different songs, with arrows connecting and noting whether they need to change or not. There is also some pseudocode in my notes. Lastly, there is a truth table on the bottom left of the page
// This is the code from a most recent version,
// it doesn’t reflect what was going on when we had the mongoDB database.
// https://github.com/Gabe-H/Gemini/blob/e3dd03ecba4d131d6abb6ef4cef8403a21ebd19e/src/info.js#L178-L195

// i just learned truth tables in discrete mathematics.
// although there's no proposition
// (with my knowledge at least, we just started this topic lol),
// at least making the table and copying it to the code
// really helped me out and simplified things.
// thanks professor stefano carpin

if (isSpecial == false && sameAlbum == false) {
  fadeOutAlbum();
  window.doesSong.haveVideo(data.body.item.uri);
}
if (isSpecial == false && sameAlbum == true) {
  window.doesSong.haveVideo(data.body.item.uri);
}
if (isSpecial == true && sameAlbum == false) {
  fadeOutAlbum();
  window.doesSong.haveVideo(data.body.item.uri);
}
if (isSpecial == true && sameAlbum == true) {
  if (!wasSpecial) {
    fadeOutAlbum();
    window.doesSong.haveVideo(data.body.item.uri);
  }
}

This made it really easy to manage whether the album art should fade out or not. The mongoDB database worked for a while, until Gabe noticed the huge slowdown in the transition between album arts throughout different songs.

Before we used mongoDB, the time it took to get the next album art for the next song was instant. But with mongoDB, the request to see if the song had a video or not took at least a second each time. I didn’t really notice it, but Gabe tried using a JSON file as our “database of song IDs with videos” hosted on GitHub. He showed me, and I realized how slow mongoDB actually was. MongoDB wasn’t appropriate for this app, but it really gave us the chance to learn about databases.

Throughout the three months we’ve spent on Gemini, you can see how far we have progressed in the programming world. Like recently, Gabe has implemented PKCE authentication so we don’t have to deal with client secrets. I also added a GitHub action that automatically builds the app for us. This was a fun—and challenging—app to code, and I’m happy we learned a lot.

If you would like to download Gemini, you can check out our releases on GitHub.