Building a Plugin-Free Playback Interface
Netflix has been running HTML5-based playback for more than a year, but we haven't said much about how the player UI itself is built. With the Silverlight experience still serving as the familiar baseline, our goal is to keep the two platforms visually and functionally aligned — releasing features on both at the same time, while giving HTML5 users faster entry into playback, 1080p support when GPU acceleration is available, and the full set of controls they expect.
That consistency comes with specific engineering requirements. To recreate the Silverlight UI in HTML5, we had to solve three core problems: scale the interface to the user's resolution, start playback with as little data as possible, and keep performance acceptable on modest hardware.
Sizing Controls Without Viewport Units
Our playback UI keeps every control at the same proportion of the screen regardless of the browser window's dimensions. That lets members pick any playback size without the controls getting in the way.
CSS viewport-relative units (vw and vh) are the obvious tool for this job, but they turned out to be a poor fit. The player doesn't always fill the viewport — it can occupy a smaller region of the page — so we needed something independent of the window. Instead, our entire sizing scheme is built on font-relative units.
The implementation treats an em as 1% of the height of the netflix-player container. Every on-screen element is then sized in those units, so the whole control set scales whether the container fills the viewport or sits inline in a document. We recalculate the size on a debounced timer rather than on every resize event, which avoids needless recalculation while the user is adjusting the window.
Starting Playback Before the UI Is Ready
Plugins such as Flash and Silverlight often need several seconds to initialize, particularly just after boot. Removing that step shortens startup time by itself, but techniques we developed for Silverlight help us go further in the HTML5 UI.
Play first, fetch metadata later
When the user has already picked a concrete title — a specific episode, say — we can begin streaming it immediately with no other knowledge. The bulk player metadata, which includes episode information and personalized predictions, is a heavy payload. By fetching it only after buffering has begun, we shorten the time before video starts by 500 to 1200ms in practice.
For trickier cases, like resuming a TV series at the last watched episode, we do resolve which episode to play before starting, since that decision needs to happen up front.
Controls that appear as data lands
The player UI must remain functional while the metadata request is pending. During that window we limit ourselves to a minimal set of controls — play/pause, exit, and fullscreen — and fill in richer controls as data arrives. An eventing system handles the decoupling: components subscribe to state changes and update themselves when relevant data shows up.
Performance Tactics for Lower-End Hardware
Not every member is watching on a high-end machine. We build and test against a range of representative devices, and we've found that most performance trouble on slower hardware comes down to a few avoidable habits.
Minimize layout thrash
Repaints and reflows are expensive during playback, both for responsiveness and battery life. We batch DOM reads and writes together wherever possible to reduce accidental reflows.
Cache measurements from getBoundingClientRect
Reading an element's dimensions via getBoundingClientRect is fast, but it isn't free. Especially during drag interactions, where measurements would otherwise be requested in rapid succession, we cache the values we need instead of recalculating each time.



