Hi everyone! I hope all of you enjoyed the holiday season and had fun during your break.
I wanted to write a small essay to describe the research I did since we last saw each other and the next steps I wanted to take with the project.
We want to build a user-based service that allows any one user to share their music, live, with everyone else who wants to listen in.
We plan to do this by using the Spotify API and various other Web technologies to provide a cohesive, cross platform solution. We would eventually monetize this service and use it as an income generator for the club.
Variations of such an idea have already been suggested in the Spotify community forums:
With thousands of people upvoting these suggestions, it seems that the market wants something like NowPlaying.
We will call this service NowPlaying. Personally I still don't think it's a good fit for us, but while it's in development the name isn't that important. What is important is to have a name that adequately reflects the primary adjectives we want to associate with our service:
If anyone has any better suggestions for a name, I'm open to hearing them.
The Spotify APIs
There's actually a bunch of them. I've compiled a list of the ones we would care about the most. Some are in beta, meaning they'll be changing around and it may not be a great idea to base a product on top of them until they've left beta stage.
Web API - Provides access to the majority of the data you can access on Spotify, such as playlists, artists, tracks, and account specific data (who you're following, your email, your library, etc.)
- The most relevant portion of this to us is the Player API (Beta), also referred to as the Spotify Connect Web API. If you're not aware, Spotify Connect is the technology that allows you to switch your playback source from your phone to your laptop/desktop/Alexa/etc. Using the web API, we can send commands to each of your Spotify devices, allowing us to remotely control what the Spotify app on every device is doing.
Web Playback API (Beta) - Lets us embed a Spotify player into our web pages. Spotify songs are encrypted through some unknown proprietary format (they're not just streamed as MP3s), and this API is an official way to play Spotify songs in web apps without requiring the native Spotify app.
- Unfortunately, this only works on desktops, as mobile devices are missing the cryptographical components that allow the Web Playback API to decrypt song streams. As far as I can tell, there are no plans to make this work with mobile devices. In addition, it will not work in Safari for Mac. See the Supported Browsers section for more information.
- "This SDK must not be used in commercial projects without Spotify's prior written approval"
iOS/Android Streaming SDK - No longer actively developed, but was the primary way to integrate Spotify into mobile apps. Offered the same functionality as the Web API and the Web Playback API combined.
...And that brings us to the latest addition to the lineup: App Remote SDK.
- App Remote SDK - Released in September 2018, so relatively recent. Its release was accompanied by this blog post on the Spotify blog. Some important paragraphs:
Earlier this year, we released beta versions of the App Remote SDK for iOS and Android. Unlike our previous playback SDKs, the Android and iOS SDK now allow you to “remote control” playback in the Spotify app. As a result, you can control playback for all users (not just Premium) and provide offline playback.
Also as of this release, the iOS SDK is out of beta. This means that we consider it to be stable, and able to be used confidently in your integrations.
The Android SDK remains in beta, and we will provide updates on this blog.
Note that these [Android and iOS] streaming SDKs are not actively maintained and no additional work on these are planned. We therefore urge you to work with the new SDKs going forward.
This is a big deal. With the old mobile SDKs gone, App Remote is what we'll have to use if we ever want to deliver native apps.
Which API For Us?
We wanted NowPlaying to be cross platform - to work on both desktop and mobile devices. We wanted to do this using their Web Playback API. But there are some big problems:
It doesn't work on mobile at all.
It can't be used for commercial projects (hopefully, us) without talking to Spotify's legal team.
So, Web Playback is out. What about Web/Spotify Connect?
It allows us to remote control the native app on desktop and mobile. And indeed, we could implement all of NowPlaying using a web app and the Connect API. But.
The API is in beta, so we have no idea if it will stick around or change heavily in later Spotify versions.
The whole API is rate limited. This, in itself, invalidates the Connect API as a choice for us.
Why not rate limiting?
Just a quick primer for those who are unfamiliar. As you are probably aware, computing is a finite resource, and companies like Spotify have to build or rent massive data centers to process all the requests they get every second. To prevent their services from failing due to overuse, Spotify implements per-second request rate limiting. This means, as a user of the Spotify Web API, you can only send X requests per second before Spotify will block your request and refuse to execute it. When this happens, you're given a
Retry-After header, which contains the amount of seconds you need to wait before Spotify will accept requests from your application again. During this duration, you cannot use the Spotify Web API at all.
I tried finding more concrete details on the limits, but it seems like they're prone to changing and undocumented. The guy in that post said to expect something like 10-20 requests / second, which at first glance seems like a lot, but when you do the math, it wouldn't work for us.
Let's assume the rate limit is set to 20 requests a second. That's 1,200 requests a minute, or 72,000 an hour, which is huge. But it's not enough for NowPlaying.
This is because NowPlaying implements live streaming. That is, for every little change you make in your Spotify app, every single one of your followers also need to have that change performed to their Spotify app. This requires at minimum one request per user.
We only get 20 requests a second. If any single person on our platform gains more than 20 followers at a time (or if more than 20 people stream at the same time that have at least one follower), we can no longer keep their followers in sync with the streamer. We would get rate limited every time they changed anything. Obviously, that's not acceptable.
The limit can apparently be lifted but I have never heard of anyone who has successfully done so, or if doing so would require entering a contract with Spotify or paying them royalties. I found this post where a Spotify employee tells the poster to contact Spotify legal, but no information on if that would cost anything.
We're not the only ones who have to deal with this problem. One of the most popular requests in Spotify's issue tracker is a way to receive real time player updates without having to poll the
Player endpoint (each poll attempt would use up a request, and we'd have to poll it at a frequency that seems akin to real time). Discord seems to have a private, custom solution built for them, but I doubt that deal was done for free.
Regardless, even if we got realtime updates, it still wouldn't solve the problem of us having to update every follower's player in real time.
So, with Web Playback and Web/Spotify Connect out, that leaves us with the mobile SDKs. And with the old iOS and Android SDKs now being unsupported, that leaves us with...App Remote.
We're Using App Remote
And with that, abandoning our plans of building a web app. App Remote is a native-only solution, so we can only use it on iOS and Android.
I wish there was an easy way to make this work on Web, but until the Web Playback SDK comes through or the rate limiting situation is drastically changed (both of which I highly doubt), this is what we have to deal with.
So now we have to build mobile apps. This is obviously a huge change in the direction of the project, which is why I wanted to write up this letter and distribute it to you all before our first meeting.
Here's how I see it working.
This part is necessary just so you understand the next section.
Typically the way computers communicate on the Web is through a protocol called HTTP. It's based on a request/response architecture, meaning that you send a request, and the server sends back a response. You can think of this as talking to Siri. When you ask Siri a question, she'll give you an answer, but Siri would never pop up on your phone unprompted and ask you a question or let you know of a new event that happened. This architecture is great for requesting documents like the Web was originally meant for, but terrible for building interactive apps since there's no way to allow for bidirectional communcation.
WebSockets is a Web standard that allows real time communication between various devices. These devices can be Web browsers, mobile phones, laptops, desktops, etc. You can think of WebSockets as texting, for computers. It's how they can have conversations with each other.
App Remote & Us
NowPlaying would be its own app that uses the App Remote SDK to talk to the Spotify app on the user's phone. When NowPlaying is opened, it establishes a WebSocket connection to our NowPlaying service and also a separate connection to the Spotify app. From here, updating player states between multiple devices is easy: just send a message to the WebSocket connection each device maintains with our service, and have the NowPlaying app forward that message to the App Remote SDK.
Here's a visualization of what a typical NowPlaying session could look like between 10 devices, where one device is streaming and the others are following.
NowPlaying App (Device 1) -> NowPlaying Service: Hello. I represent Spotify user <whoever is logged into Spotify on that device>.
NowPlaying App (Device 1) -> App Remote SDK -> Spotify App: What song are we playing right now?
Spotify App -> App Remote SDK -> NowPlaying App (Device 1): We're playing "Thank You For The Music" by ABBA, currently at 0:38.
NowPlaying App (Device 1) -> NowPlaying Service: I want to stream now. I'm streaming "Thank You For The Music" by ABBA, currently at 0:38.
NowPlaying App (Devices 2...10) -> NowPlaying Service: I want to follow Device 1!
NowPlaying Service -> NowPlaying App (Devices 2..10): Okay! Device 1 is currently streaming "Thank You For The Music" by ABBA, currently at 0:38.
NowPlaying App (Devices 2..10) -> App Remote SDK -> Spotify App: Play "Thank You For The Music" by ABBA, starting from 0:38.
...And if the user on Device 1 changes something in the Spotify app, say they skip ahead:
Spotify App -> App Remote SDK -> NowPlaying App (Device 1): Heads up. The user changed the player state. We're now playing "Thank You For The Music" by ABBA at 1:12, not 0:38 anymore.
NowPlaying App (Device 1) -> NowPlaying Service: Hey. I'm changing my stream. I'm streaming "Thank You For The Music" by ABBA at 1:12 now.
NowPlaying Service -> NowPlaying App (Devices 2..10): Device 1 is changing their stream. They're going to play "Thank You For The Music" by ABBA at 1:12 now.
NowPlaying App (Devices 2..10) -> App Remote SDK -> Spotify App: Change of plans. Play "Thank You For The Music" by ABBA at 1:12 now.
Because we're not directly touching Spotify's services, there is no rate limit. This is infinitely scalable, and we don't need to pay Spotify anything to use the App Remote SDK or WebSockets between our own private apps.
Hosting The WebSocket Server
There are a variety of ways to do this but I am leaning towards AWS API Gateway which just recently came out with WS support. API Gateway has no servers involved, which reduces the complexity of running our service by a huge magnitude. We pay for connection minutes (number of minutes each client is connected) and total number of requests. You can look at the pricing tab for more information, but the basic gist is that we pay $1 per million requests and $0.25 per million connection minutes. I just used API Gateway to implement live chat on my own personal website, so I now have enough experience to figure out how to make it work for NowPlaying as well.
Building The Mobile App
It's important to know that the App Remote SDK itself only works fully on iOS. The Android counterpart is in beta, meaning it's being actively developed, but will take a bit to reach a stable stage.
When I set out to build NowPlaying over break, I realized I would have to build that bridge myself. And while I broke some ground to try to get the two frameworks co-operating, I soon found react-native-spotify which appears to be a more complete, community driven Spotify API implementation. Right now it conforms to the old streaming SDK so it's not immediately useful to us but work is underway to get it up to date with the new App Remote SDK.
Once this project is complete within the next few weeks, we will have everything required to implement NowPlaying in a simple, straightforward, scalable, and long lasting manner.
NowPlaying is switching from a Web app to a mobile app. This not only allows us to leverage the App Remote SDK, avoid rate limiting, and provide a real, truly live experience, but it opens up the possibility of using other technologies in the future, such as push notifications and geolocation tracking.
We are going to use AWS API Gateway to offer a WebSocket service to connect our various clients together and build the business logic that makes NowPlaying possible.
Our clients will be React Native applications that use the Spotify App Remote SDK to talk to the Spotify app on each user's device. They'll connect to the WebSocket service to receive orders and allow us to remote control Spotify.
We are waiting on the
react-native-spotify package to finish adding the App Remote SDK before starting development on the mobile app. Once that package implements App Remote, we can finally break ground and bring the idea to reality.
This is the plan as I see it. Of course I'd love to gather feedback from you all to decide how to proceed. Once again, I hope you had a great break and I look forward to discussing this in person at our next meeting.
Want more where that came from? Sign up to the newsletter!