System design

Design a Video Player (Netflix/YouTube-style)

← Back to System design

Difficulty: 🔴 Hard · Est. time: 1h · Tags: #media #streaming #performance #a11y

Asked at: Netflix, YouTube/Google, Meta, Amazon · Related: Image Carousel · News Feed


1. The Question

Design a custom video player: play/pause, seek, volume, quality selection, captions, fullscreen — with adaptive streaming so quality adjusts to the network.

2. Requirements

Functional

Non-functional

3. High-Level Design

┌──────────────┐   segments    ┌─────────┐
│ ABR engine   │ ◀──────────── │  CDN    │  (HLS/DASH manifests + chunks)
│ (hls.js/dash)│               └─────────┘
│  picks bitrate by bandwidth + buffer
└──────┬───────┘
       ▼ appends to
┌──────────────────────────┐
│ <video> + MediaSource     │  ← core element
│ (SourceBuffers)           │
└──────┬───────────────────┘
       ▼ state
[Controls UI]  play·seek·volume·quality·captions·fullscreen
  • <video> + Media Source Extensions (MSE): JS feeds media segments into SourceBuffers, enabling adaptive streaming (native <video src> can't switch bitrates mid-stream).
  • ABR engine (hls.js / dash.js / Shaka) picks the next segment's bitrate from measured bandwidth + buffer health.
  • Controls are a custom overlay reading player state.

4. Deep Dives & Trade-offs

Progressive download vs adaptive streaming → a single MP4 (<video src>) is simple but can't adapt to bandwidth and wastes data. HLS/DASH over MSE splits video into short segments at multiple bitrates; the player switches per-segment. This is why Netflix/YouTube use it.

ABR algorithm → choose bitrate from (a) throughput estimate and (b) buffer occupancy. Buffer-based (BOLA) avoids overreacting to throughput spikes. Trade-off: aggressive upshift = higher quality but more rebuffer risk.

Buffering strategy → keep a target buffer (e.g. 30s ahead); show buffered ranges on the seek bar (video.buffered). On seek, may need to fetch/append new segments.

Controls state → drive UI from the <video> element's events (timeupdate, progress, waiting, ratechange) — the element is the source of truth, not parallel React state.

Captions → use <track kind="subtitles"> (WebVTT) for native rendering + accessibility, or render custom cues for styling control. Must be toggleable and language-selectable.

Accessibility → keyboard shortcuts (Space=play, ←/→=seek, ↑/↓=volume, F=fullscreen, C=captions); controls are real buttons with labels; captions are first-class; respect prefers-reduced-motion for animated UI. Seek bar is a slider with aria-valuenow.

Performance → lazy-init the player when in viewport; preload metadata only; abort segment fetches on unmount; free SourceBuffers to avoid memory growth on long sessions.

Thumbnails on hover → a sprite sheet (BIF/WebVTT thumbnails) mapped to timestamps.

5. What Interviewers Probe

  • Why MSE/HLS/DASH instead of a plain <video src>?
  • How adaptive bitrate selection works (throughput vs buffer-based).
  • Driving controls from the media element's events.
  • Captions and full keyboard accessibility.
  • Buffer management and seek handling.
  • Memory over long playback sessions.

6. Curated Resources

ESC

Type to search across every question bank.

navigate open 3,610 questions indexed