Published on October 19, 2022 (almost 2 years ago)

Slow is stable, stable is fast: building Mux Player on the “slow” platform of web components

Dylan Jhaveri
By Dylan Jhaveri13 min readEngineering

There have been a lot of hot takes and chatter flying around the interwebs about web components over the last few months. We read a lot of it, but the day-to-day 280-character hot takes on Twitter are not very helpful when our goal is to build an SDK for the web that we plan to support for decades.

Instead of hot takes, we’re talking about what all software engineering comes down to: trade-offs, decision-making, and choosing the optimal path (not the perfect path) while balancing development resources, product requirements, and long-term goals.

When you start thinking about building something that folks can use in their web applications for many years, the noise starts to fade away, and the right technology choices, which started off blurry, begin to come into focus.

LinkMeeting developers where they are

There are a lot of SDKs that developers use on a daily basis that they simply tolerate because…they have to.

That’s a low bar.

We think developers deserve better. We wanted to build a Mux Player that developers don’t just tolerate, but that they love and tell their friends about. One of our values at Mux is to turn customers into fans. How can Mux Player be one of the factors that turns a passive customer into a fan?

The first principle we think about when building an SDK — and one that developers love — is to meet developers where they are. Integrating Mux Player into a web application, no matter what the stack, should feel natural. It should install easily and work with whatever tooling you’re using. Code that you write to interact with an SDK should look and feel just like your application code. Developers should be able to use an SDK in a way that is idiomatic with their language or framework of choice.

In order to meet developers where they are, let’s take a quick look at the front-end development landscape as it exists in 2022.

React is still extremely popular, but it’s not the only game in town. If we only cared about supporting React developers for the coming decades, the choice would be simple: build a pure React component and ignore everything else.

But that’s not good enough. Having great support for the React ecosystem is a firm requirement, but we can’t stop there. We’ve seen a number of teams recently move away from React and back to good old server-rendered HTML with full-stack frameworks written in Ruby, Python, Elixir, or PHP. Forcing them to use a React component is not meeting them where they are. Mux has to go bigger than React.

In addition to the old full-stack frameworks, there’s been a rapid increase in new frameworks focused on sending pure HTML over the wire. See the recent buzz around bun.sh, deno, Enhance, and astro.build. And of course, the popular non-React frameworks like Vue, Svelte, SolidJS, etc.

We must support all of these. As a small team, we asked ourselves: How can we build a video player SDK that feels native and natural in all these web applications? Our answer is web components.

LinkWeb components move slow (and slow is stable)

If you’re used to the world of front-end frameworks, you’ll feel that web components move incredibly slow. Despite people saying since 2013 that web components are the future, things do not change overnight.

Why? Because web components are a standard, not a library. Web components APIs are built directly into browsers. Updates to the web components require coordination between two standards bodies, WHATWAG (Web Hypertext Application Technology Working Group) and W3C, to update 3 specifications: HTML, DOM, and CSS. For many years, the relevant specs were in draft and proposal stages; over the past several years since their approval, they have been gradually adopted by browser vendors.

Why is slow good for us? Because slow is stable. We can write code with confidence today, knowing it’s going to work across web frameworks for a very long time. Once something makes it into the spec and browsers adopt it, it’s really hard to change it.

As with everything in software development, that benefits come with trade-offs. The downside is that new features and new functionality for web components are going to happen at a slower pace compared with new features in React, Svelte, Vue, or your favorite framework. We are willing to accept that web component specs move slowly in exchange for the benefit that we don’t have to keep rebuilding our SDK with the same functionality every time a new front-end framework comes out.

The speed at which front-end frameworks evolve doesn’t really matter much to us, because our core technology relies solely on browser primitives. We’ll have no problem making sure Mux Player works with whatever tech stack happens to be popular 5 or 10 years from now. At worst, we’ll need to build some wrapper SDKs or add some syntactic sugar, but the lowest common denominator is still browsers — and that’s what we’re targeting.

LinkLayers of abstraction

The second principle for building an SDK that developers love is to think about layers of abstraction. The simplest implementation should be the highest level of abstraction, requiring the minimum amount of code for the most amount of functionality. It’s the batteries-included, so-easy-it’s-hard-to-mess-up implementation.

This simple implementation with a minimal amount of code is how you expect most developers to use your SDK. And when they’re going down the simple happy path, don’t expect folks to be reading the docs. They shouldn’t need to read the docs at all. Copy-pasta solutions are the goal at this stage, and ideally, most developers start here and stay here.

For the developers who do want to move beyond the simple use case, that’s where additional, deeper layers of abstraction come into play.

We’ve thought a lot about Mux Player layers, and this is where we’ve landed:

  1. Simple happy path with <mux-player>: One line of code gives you everything. With minimal docs interaction, you can copy/paste a few things and be on your way with a fully featured player. About 50% of Mux Customers are done after this step.
  2. Customizing Mux Player: There are several layers of customization, starting with the primary-color attribute. If you want to customize what controls are showing, you can do that with CSS variables. If you want extra, granular control with CSS, the individual components are exported as CSS parts. If you need to hook into some events, you can do that too. For the next 35% of Mux Customers, this will be more than enough customization.

For the last 15% of Mux customers who need more, our intentional choices come into play with deeper layers of abstraction. Most folks using Mux Player never have to think about this at all, but for the people who do (or want to), this becomes extremely powerful:

  • <mux-video>: This is the Mux-flavored HTML5 <video> element. It’s a Mux-specific solution that handles video playback and Mux Data integration. Think of it as the core playback engine with no UI beyond the browser’s built-in video controls.
  • Media Chrome: this is a UI library for building players. This is NOT Mux-specific at all. Media Chrome is purely UI; it handles none of the video playback itself. Developers can use Media Chrome to build video player UIs with any playback engine (like <mux-video> or hls.js).
  • Castable Video extends the native HTMLVideoElement to add Chromecast functionality. Castable Video is not Mux-specific. It can be used with any element that speaks the HTMLVideoElement API.

These layers all come together to create Mux Player, the player that developers love. And better yet, every piece is open source and MIT licensed. You’re free to see all the code, open issues, comment, and fork it to make your own modifications.

The vast majority of folks using Mux Player never have to know about these layers — but for the super-users who want to go deeper, this unlocks everything they need. If you don’t want any of the UI that Mux Player provides and instead want to build all the individual controls yourself, then you can use <mux-video> + Media Chrome to build out a completely custom player. And then layer in Castable Video for Chromecast capabilities on your player.

LinkMeeting accessibility requirements

When building an SDK that will be used across all of our customers’ applications, another big consideration is to make sure Mux Player is as accessible as possible according to WCAG guidelines. Video players tend to have some common shortcomings in this department.

Based on what we’ve seen, some common issues are:

  • Timeline ranges are unusable with a physical switch: We solve this by using an input type="range". This has some other trade-offs, but on the positive side, it plays nice with assistive technologies by default.
  • Controls obscure captions: This is a tough one. Like other players, Mux Player has controls at the bottom of the player. Well, that happens to be exactly where captions tend to appear. Unfortunately, it’s pretty common that when the controls are shown, they obscure captions, which degrades the experience for users relying on captions. Mux Player solves this problem by shifting the captions up when the control bar is visible.
  • Captions aren’t rendered correctly: Speaking of captions, some players render captions in the simplest terms, ignoring any cue settings. They’ll use getCueAsHTML(), throw the captions in a <div>, and call it a day. Mux Player uses the browser’s native captions renderer, which respects users’ OS-level and browser-level preferences such as font sizes, colors, etc.
  • Contrast ratio requirements do not meet accessibility standards: Some feedback we’ve gotten is that the overlay behind the controls looks too dark. For some folks, that’s their initial reaction, but we’ve carefully selected the shade of the overlay so that the controls satisfy the contrast ratio requirements for WCAG 2.1.
  • Incompatible with screen readers: Often, video players are not thoroughly tested with screen readers, which results in inaccurate or incomplete announcing of controls. We made sure to do our best here and not overlook the experience for users who rely on screen readers to access the web.

When developing Mux Player, we used the Firefox and Chrome built-in accessibility dev tools and MacOS Voice Over. To get some more extensive testing after building out the initial version of Mux Player, we contracted with an agency that specializes in accessibility auditing. This allowed us to test with real hardware (JAWS 2022) and with users who rely on assistive technology.

LinkTying it all together

You can drop Mux Player into any web application with one line of code and get access to the entire Mux Platform. It looks like this:

html
<mux-player stream-type="on-demand" playback-id="FuJSYrK0014ec2LPnm11bzC2MAySAQPqA" metadata-video-title="Tea and Cookies" metadata-viewer-user-id="user-id-007" ></mux-player>

That one line of code is extremely powerful. It comes with a responsive design that looks good at any size, adaptive bitrate streaming, keyboard shortcuts, fullscreen, picture-in-picture, Chromecast, AirPlay, and Mux Data for measuring your video Quality of Experience (see the player page and the docs for a full rundown of features).

We initially thought we might hand-roll a wrapper component for all the frameworks and use the web component as a sort of shared common denominator. This seemed like a lot of work, but also doable if we felt it was definitely necessary.

First up, React. The same component, published as a standalone package @mux/mux-player-react and with an interface that feels idiomatically React.

jsx
<MuxPlayer streamType="on-demand" playbackId="FuJSYrK0014ec2LPnm11bzC2MAySAQPqA" metadata={{ video_title: "Tea and Cookies", user_id: "user-id-007" }} onPlay={() => console.log(‘playing’)} / >

We’re holding out for the planned future versions of React to support web components better, but until then, we’re perfectly happy maintaining a nice <MuxPlayer /> React component. It’s a fairly thin wrapper, translating React props into HTML attributes and event names like onPlay to addEventListener('play'). The current initiative for React + web components is currently available on the experimental branch. It passes all the custom-elements-everywhere tests and is slated for React 19.

We were pleasantly surprised to find that the other major frameworks out there have good support for web components. Using the Mux Player component in Svelte, Angular, and Vue all felt natural. We might add wrapper SDKs for those frameworks later, but for now we feel like it’s not necessary.

LinkIn the wild

You can find Mux Player today in the dashboard of many of our partner CMS integrations, including Sanity, Contentful, and Strapi. For Contentful and Strapi CMS users, we've added Mux Player for previewing your videos right in the editor's view alongside a copy/paste-able code snippet for adding Mux Player to your application.

Mux Player is also the default player on stream.new, the Mux dashboard, mux.com, docs.mux.com, and most of our fun example projects.If you’re interested in using Mux Player, you can start here on the player page and in the docs. All source code for Mux player lives in the elements repo.

And of course, you can peel back the layers of abstraction that Mux Player is built on with Media Chrome, Castable Video, and the <mux-video> element.

Play on, developers.


Written By

Dylan Jhaveri

Software Engineer and cold water surfer. Previously startup co-founder. Trying to find the best cheeseburger in San Francisco.

Leave your wallet where it is

No credit card required to get started.