Building the Hulu Experience in the Living Room
By Kirk Yoder, Software Developer
If you’ve been following along with the blog, in our first post Andrew provided a great high-level overview of the technical landscape here at Hulu. In his post, Andrew mentioned that we currently support the Hulu experience on upwards of 20 “client” — or device — platforms ranging from gaming consoles to streaming devices, mobile platforms, Smart TVs, and even virtual reality devices like Oculus.
Certain client platforms, like iOS, tvOS (Apple TV), and Android have dedicated engineering teams that build and maintain native Hulu applications for their respective ecosystems. However, nearly half of all of the remaining client platforms are actually maintained by a single engineering team. Aptly named, the “Living Room” team is responsible for building Hulu’s “10-foot experience” for consumer devices that are most commonly found in our viewers’ living rooms, including: the Xbox One, Xbox 360, Playstation 4, Playstation 3, Nintendo Switch, and the Amazon FireTV.
The Living Room team’s charter is to onboard new “10-foot” platforms into the Hulu ecosystem, as well as improve the Hulu experience on existing ones. Every two weeks, our team builds, tests, and releases updates based on viewer feedback and engagement data. Unlike the other engineering teams that I mentioned, though, we aren’t focused on a single platform… we’re focused on all of them. With the occasional exception of some device classes that are deferred from an update, in general, the Living Room team releases updates to our entire suite of device platforms in parallel.
Supporting this scale of iteration across so many unique device platforms, while still continuing to bring new ones into our ecosystem, presented a unique challenge to our small team of fewer than thirty developers. Our solution: a single codebase that supports our entire device ecosystem. For Living Room devices, the Hulu experience that users are interacting with is actually a fully hosted modern web application written in HTML and JavaScript, made portable across our suite of supported devices by means of a home-built platform abstraction layer called the “Client Device Platform”.
In this post, I’m going to discuss how the Client Device Platform (CDP) works and explain how we leverage it to support our engineering lifecycle and update our platform experiences en masse.
Doing the Heavy Lifting — How CDP Helps Us Write Once and Deploy Everywhere
The Client Device Platform (CDP) does a lot of heavy lifting for our web application, and there’ll be future blog posts that go more in-depth on the full range of functionality (spoiler: it’s a lot) that it provides. At its core, we built CDP to abstract away specific details of the device platform on which it’s running and provide a common and stable interface for our application. This paradigm is very similar to the approaches taken by development frameworks like PhoneGap or Titanium which enable developers to create hybrid applications using HTML, CSS, and JavaScript that can then run on a wide variety of mobile platforms with completely different architectures.
This approach has a number of advantages: the biggest of which is that it enables us to utilize a single application that is portable to any device that CDP supports — anything from an Xbox One to a Fire TV — we’re able to write once and deploy everywhere. This means that our team can spend more time enhancing the application experience for our viewers and less time bogged down by maintaining feature parity across our ecosystem.
Interacting with the Native Device via JavaScript
The Hulu web application leverages a JavaScript interface to interact with a native device platform, like the Xbox One or Nintendo Switch. The interface — internally referred to as the “JavaScript Bridge” (JSB) — is exposed to our web application by means of a specially customized fork of Webkit that’s baked into CDP. This Webkit variant has a number of tweaks to its WebCore and JavaScriptCore components that optimize media streaming, but, most importantly, it’s responsible for rendering our hosted web application on each device platform.
The JSB acts as an intermediary to negotiate interactions between the consuming web application and the native platform atop of which CDP is running. It’s responsible for translating any method calls made to one of its interfaces to the appropriate native API for the platform, as well as ensuring that any native platform events are bubbled back up to the appropriate event on the interface. This enables the consuming web application to listen for and react to changes on the platform without ever needing to know anything about the actual platform’s API surface.
Whenever a viewer launches the Hulu application on their device, they’re actually launching the CDP binary for that platform. After performing any necessary bootstrapping that may be required by the device, CDP will spin up its internal Webkit variant and issue network requests to load the appropriate device-specific Hulu web application endpoint and render the actual user experience.
The web application will attach standard JavaScript event listeners, as necessary, to react to any platform events that are being surfaced through the JSB interface. Changes in network connectivity or average throughput, for example, can be observed through the common “connection_change” event:
The web application itself can now also use the JSB to issue commands to the platform’s own operating system.
CDP in Action: Invoking a Native Keyboard Overlay from the Application
Consider a scenario where we’d like the viewer to input a string of text inside of our web application. While we have written our own on-screen keyboard UI that we’ll fall back to in the event a device doesn’t provide one, we prefer to let viewers enter text using their own platform’s native controls whenever possible. This typically leads to a more consistent (and pleasant) viewer experience for them.
Every device platform has its own unique API surface and workflow for both displaying an on-screen keyboard as well as receiving user input from it. With CDP, however, our web application doesn’t need to worry about any of that. We simply call the relevant APIs exposed by the JSB in order to trigger the on-screen keyboard and then listen for any user input:
The appropriate native platform APIs are called by CDP and the on-screen keyboard is displayed to the viewer. The viewer is all the merrier because they aren’t forced to learn another new keyboard layout, and our lives are made easier because we don’t need to write specific logic for handling keyboard scenarios across each of the device platforms in our ecosystem.
Our application utilizes this bridge for all manner of interactions between the Hulu experience and the device platform: everything from fetching the device’s screen resolution to triggering device-specific payment workflows.
Bringing the Hulu Experience to a New Device Platform
For other applications, launching their experience on a new device can often mean tasking an engineering team to build an entirely new native application for that platform. As we onboard new device platforms into our ecosystem, however, there are very few changes that need to be made to our actual application logic, as it’s designed to run atop CDP, not a particular native platform.
We simply work with the CDP team during device bring-up to ensure that all of the necessary platform hooks exposed by the JSB are appropriately wired up to their corresponding native APIs on the new device. Once that work happens, our existing web application is able to run on the new platform, with relatively little engineering effort on our part.
This approach saves us an immense amount of time, both in terms of development and quality assurance. Because each platform is running the same application, we can be smarter about how we develop test matrices — there’s no need to run the same suite of tests across each platform, as any one of them would expose a defect if it exists. Whenever defects are discovered, a single fix also addresses the issue on all platforms.
There are a few exceptions: you might recall from my earlier introduction to the Living Room team that there are a number of devices inside of Hulu, including iOS, tvOS, Android, and Roku, whose client experiences are not built upon the Client Device Platform. These platforms, in particular, benefit immensely from having their client experiences built as native applications — particularly when it comes to performance and access to native UX components. By building our applications as close as possible to each of these devices’ native layers, we’re able to better optimize the experience for our viewers. There’s clearly a tradeoff, however, and we regularly weigh in on the pros and cons of pulling one or more of these devices into our common platform development strategy.
The Client Device Platform is at the very core of how we’re able to sustain our release cadence across so many unique device platforms. Thanks to the platform abstractions that it provides, our web application is able to remain portable and largely platform agnostic — and we’re freed from having to maintain separate codebases for each device class that our application supports.
Because our web application is able to run on any client platform that CDP can support, we’re able to expand our application’s footprint to a more diverse range of device classes without incurring the cost of building and maintaining separate codebases. Not only does this support our rapid engineering lifecycle, but it helps to ensure that we minimize feature fragmentation across the majority of the living room devices that we support: that hot new functionality on the Nintendo Switch will also be available on the Xbox One and the FireTV variants of our application.
Now you have a glimpse into how the Living Room team uses a layered architecture to build the Hulu experience: a portable web application that runs atop a common device platform that abstracts away all of the device-specific nuances. This approach supports rapid iteration across the breadth of our devices and enables us to continually improve the application experience for our viewers.
Kirk Yoder is a software engineer working on Hulu’s Living Room team. He is a member of the Browse vertical team, which is responsible for building out the visual and interactive experiences of the Hulu client application, specifically those experiences that relate to content discovery. He comes from a predominantly full-stack web development background. Prior to joining Hulu, he spent several years at Microsoft as an engineer on the team responsible for developing the platform shell experience on the Xbox One.