<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hiếu Cao Khả</title>
    <description>The latest articles on DEV Community by Hiếu Cao Khả (@hiu_caokh_3472336e2d90).</description>
    <link>https://dev.to/hiu_caokh_3472336e2d90</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3496933%2F49237d1f-c078-4d65-b878-949804b8aa26.jpg</url>
      <title>DEV Community: Hiếu Cao Khả</title>
      <link>https://dev.to/hiu_caokh_3472336e2d90</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hiu_caokh_3472336e2d90"/>
    <language>en</language>
    <item>
      <title>How YouTube Downloads Videos and Plays Them Offline with Javascript?</title>
      <dc:creator>Hiếu Cao Khả</dc:creator>
      <pubDate>Fri, 12 Sep 2025 08:56:51 +0000</pubDate>
      <link>https://dev.to/hiu_caokh_3472336e2d90/how-youtube-downloads-videos-and-plays-them-offline-with-javascript-2f8h</link>
      <guid>https://dev.to/hiu_caokh_3472336e2d90/how-youtube-downloads-videos-and-plays-them-offline-with-javascript-2f8h</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Have you ever wondered how YouTube allows you to download videos and watch them offline? It's not just about saving a file to your device. YouTube employs a sophisticated mechanism involving **HLS streaming, IndexedDB storage and Uint8Array for binary handling. Let’s dive into the technical magic behind this feature! 🚀&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding HLS Streaming
&lt;/h2&gt;

&lt;p&gt;Many video on the interneet uses &lt;strong&gt;HTTP Live Streaming (HLS)&lt;/strong&gt; to deliver video content efficiently. Unlike traditional downloads, HLS breaks videos into &lt;strong&gt;small &lt;code&gt;.ts&lt;/code&gt; segments&lt;/strong&gt; and provides a manifest file (&lt;code&gt;.m3u8&lt;/code&gt;) that guides playback. This approach allows for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Adaptive streaming&lt;/strong&gt;, where video quality adjusts based on network speed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Efficient loading&lt;/strong&gt;, as only required segments are fetched, reducing bandwidth usage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smooth seeking&lt;/strong&gt;, since video chunks are separate, allowing fast-forwarding without preloading the entire video.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Example: HLS manifest file (&lt;code&gt;.m3u8&lt;/code&gt;)&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360
video_360p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1400000,RESOLUTION=1280x720
video_720p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1920x1080
video_1080p.m3u8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💡 Now that we understand how YouTube streams videos, where does it store them for offline viewing?&lt;/p&gt;




&lt;h2&gt;
  
  
  Where Does YouTube Store Downloaded Videos? IndexedDB
&lt;/h2&gt;

&lt;p&gt;When you download a video on YouTube, it doesn’t get saved as a simple &lt;code&gt;.mp4&lt;/code&gt; file. Instead, &lt;strong&gt;YouTube uses IndexedDB&lt;/strong&gt;, a built-in browser database, to store video data efficiently. Here's why:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IndexedDB allows storage of &lt;strong&gt;large binary files&lt;/strong&gt;, making it perfect for video chunks.&lt;/li&gt;
&lt;li&gt;It supports &lt;strong&gt;structured data storage&lt;/strong&gt;, which helps manage video metadata, resolutions, and playback positions.&lt;/li&gt;
&lt;li&gt;Unlike &lt;code&gt;localStorage&lt;/code&gt; or &lt;code&gt;sessionStorage&lt;/code&gt;, IndexedDB has no strict size limitations, making it suitable for &lt;strong&gt;offline video caching&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example: Storing video chunks in IndexedDB
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;indexedDB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YouTubeCache&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onupgradeneeded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createObjectStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;videos&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;keyPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💡 Great! Now we know where the data is stored. But what format does YouTube use to save these video segments?&lt;/p&gt;




&lt;h2&gt;
  
  
  Uint8Array: Handling Video Data in the Browser
&lt;/h2&gt;

&lt;p&gt;Instead of saving video files as-is, YouTube stores them as &lt;strong&gt;Uint8Array&lt;/strong&gt;, a special typed array in JavaScript designed for handling raw binary data. Why Uint8Array?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compact and efficient storage&lt;/strong&gt;: Stores video as byte sequences without unnecessary overhead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast retrieval and manipulation&lt;/strong&gt;: Allows quick access and processing before playback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ideal for streaming&lt;/strong&gt;: Works well with Media Source Extensions (MSE) to reconstruct videos dynamically.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example: Converting a Blob to Uint8Array
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;video-segment.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uint8Array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Video chunk as Uint8Array:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;uint8Array&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💡 Now that YouTube has downloaded the video data, how does it actually play the video from IndexedDB?&lt;/p&gt;




&lt;h2&gt;
  
  
  Loading and Playing Video from Uint8Array using mux.js
&lt;/h2&gt;

&lt;p&gt;I don't know how YouTube plays videos offline, but I uses &lt;strong&gt;mux.js&lt;/strong&gt;, a JavaScript library designed for working with media container formats like MP4 and TS, to reconstruct and play stored videos. Here's how it works:&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;Check it out on GitHub:&lt;/strong&gt; &lt;a href="https://github.com/videojs/mux.js" rel="noopener noreferrer"&gt;mux.js&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Retrieving stored video data&lt;/strong&gt;: YouTube fetches Uint8Array chunks from IndexedDB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repackaging with mux.js&lt;/strong&gt;: The extracted data is &lt;strong&gt;converted into a playable MP4 format&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming to HTML5 video player&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Uses &lt;strong&gt;Media Source Extensions (MSE)&lt;/strong&gt; to append processed video chunks.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag dynamically loads and plays the reconstructed video.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Example: Playing a video from Uint8Array with mux.js
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;muxjs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mux.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transmuxer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;muxjs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mp4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Transmuxer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;video-segment.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;transmuxer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nx"&gt;transmuxer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💡 We’ve now completed the journey from downloading a video to playing it back offline! 🎬&lt;/p&gt;




&lt;h2&gt;
  
  
  My Library for Supporting Download and Offline Playback
&lt;/h2&gt;

&lt;p&gt;To help developers implement a similar offline video solution, I created a library called &lt;strong&gt;hls-downloader&lt;/strong&gt;. This library makes it easy to &lt;strong&gt;download HLS video segments&lt;/strong&gt; and store them for offline playback.&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;Check it out on GitHub:&lt;/strong&gt; &lt;a href="https://github.com/CaoKhaHieu/hls-downloader" rel="noopener noreferrer"&gt;hls-downloader&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;Check it out on NPM:&lt;/strong&gt; &lt;a href="https://www.npmjs.com/package/@caokhahieu/hls-downloader" rel="noopener noreferrer"&gt;hls-downloader&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this library, you can efficiently download HLS streams, store them in IndexedDB, and play them back seamlessly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion: The Magic Behind YouTube’s Offline Mode
&lt;/h2&gt;

&lt;p&gt;YouTube’s offline video playback relies on a combination of **HLS streaming, IndexedDB for storage and Uint8Array for efficient binary handling. Each of these components plays a crucial role in ensuring smooth, high-quality offline video experiences.&lt;/p&gt;

&lt;p&gt;Understanding this process can help developers create &lt;strong&gt;similar offline video applications&lt;/strong&gt; or optimize video playback on the web.&lt;/p&gt;

&lt;p&gt;🚀 Inspired to build something similar? Dive into these technologies and start experimenting!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>youtube</category>
      <category>hls</category>
    </item>
  </channel>
</rss>
