Podcasts Without Apps
By: Alastair Coote
Tags: JavaScript, Podcast, Mobile Web, Innovation, Mobile Development

Podcasts Without Apps

Building a fully featured podcast player for mobile web.

Photo: Teri Pengilley

We’re all so programmed to appreciate the conveniences that smartphone apps provide us that it’s easy to forget how much of a barrier they can be to moving through everyday activities on a phone. In the middle of a web search, a conversation or a journey, a user can be diverted to an app store, wait for an app to download, then get dumped at a launch screen totally unrelated to their original activity.

But there are ways around this. The lab has experimented extensively with the web Notification and Push APIs, and even though they’re still Android-only, it’s been fascinating to see how easily you can build an audience on the web when you remove the barrier of having to download and use an app. We recently took a trip to the Guardian offices in London that coincided with a Hack Day they were running, and I took the day to explore how we might combine these and other web APIs in an area that’s booming right now: podcasts.

Why podcasts on mobile web?

For one, many Android phones, unlike iOS devices, don’t come preloaded with a podcast app. Which means podcast producers have to rely on users finding an app on their own or being recommended one to download. Otherwise, publishers might promote discovery by integrating podcasting functionality into their own native apps. All pose significant barriers to gaining an audience. Plus, the fact that iOS does have a podcast app means that the Android-only nature of these web APIs isn’t a hindrance.

Also, it’s timely. Google have just announced support for the Media Session API in Chrome 57, and it seemed like the ideal size to take on in a hack day.

Part 1: Media Session API

This was the easiest part to implement, as it doesn’t require a Service Worker or any extra hooks other than an extra few lines of JavaScript. Whenever an <audio> or <video> tag starts playing in Chrome, a notification is created to allow users to pause and stop. This notification can be augmented with two basic pieces of functionality — metadata, and event hooks. First, in order to add an image, title and artist to our podcast display, we add this:

navigator.mediaSession.metadata = new MediaMetadata({

title: "The people's memes: how social media and populism are

changing our lives – tech podcast",

artist: "Technology",

artwork: [

{

src: "https://www.theguardian.com/thumbnail.png",

sizes: "500x500",

type: "image/png"

}

]

});

However, play controls aren’t added to the notification until we add event listeners, like so:

let audioTag = document.getElementById("theTagWeWantToPlay"); function seek(amount) {

audioTag.currentTime = audioTag.currentTime + amount;

} navigator.mediaSession.setActionHandler("play", () =>

audioTag.play()

); navigator.mediaSession.setActionHandler("pause", () =>

audioTag.pause()

); navigator.mediaSession.setActionHandler("seekbackward", () =>

seek(-15)

); navigator.mediaSession.setActionHandler("seekforward", () =>

seek(15)

); navigator.mediaSession.setActionHandler("previoustrack", () =>

seek(-60)

); navigator.mediaSession.setActionHandler("nexttrack", () =>

seek(60)

);

The good news is that these are generic event handlers that let you do anything in response to taps — in this instance, because we’re dealing with a podcast, I set the skip forward and skip track buttons to move 15 seconds and one minute respectively. Behold, our new player notification:

Part 2: Subscriptions

This part is more involved, as it necessitates creating a service worker. But it’s still not that complicated — once we’ve requested permission to send notifications to the user using Notification.requestPermission():

I can’t recommend ngrok enough for testing your localhost code on mobile devices (especially as it provides HTTPS for free, which Service Workers require)

We use the lab’s service-worker-command-bridge library to send a subscribe request from the web page to the worker. The worker will then get a phone-unique notification identifier using PushManager.getSubscription(), and send that to any web notification service — in this case, Google Firebase Cloud Messaging. We use its topic-based messaging to sign the user up for a topic specific to the podcast they’re listening to.

Now we’ve able to send a message to that topic whenever a new episode is available, and present a notification to the user prompting them to listen:

And there we have it — a subscription-based podcast player that never leaves your web site. Zero friction, and hopefully far less confusing to users who don’t normally listen to podcasts.

What we can’t do… yet

Of course, there’s some functionality missing here. There’s some good news and bad news on that front:

Downloading episodes

One of the really useful features of a podcast player is that it downloads the episode when you’re on wi-fi so it’s available instantly whenever you want to listen, whether you’re online or off. It is possible to add an item to the service worker cache when we receive the notification of a new episode, but it’s not really designed for large files. If the download takes too long the worker gets terminated, and you can’t limit the download to wi-fi only. But change is afoot — Google has proposed new service worker functionality called Background Fetch which will allow us to do all this and more. One to keep an eye on.

Play directly from the notification

One small UI concern — even when we have Background Fetch implemented in browser, we still won’t be able to start playback without opening a browser window and waiting for the user to tap on the play button. There is no way to play an audio file from inside a service worker. Ideally we would be able to manually create a MediaSession, providing the same metadata along with source information. There are no plans to implement this so far, though.