[{"data":1,"prerenderedAt":1054},["ShallowReactive",2],{"content:\u002Fimage-media-optimization\u002Fvideo-performance-optimization\u002Fhls-streaming-vs-progressive-mp4":3,"surroundings:\u002Fimage-media-optimization\u002Fvideo-performance-optimization\u002Fhls-streaming-vs-progressive-mp4":1046},{"id":4,"title":5,"body":6,"description":1025,"extension":1026,"meta":1027,"navigation":1039,"path":1040,"seo":1041,"stem":1044,"__hash__":1045},"content\u002Fimage-media-optimization\u002Fvideo-performance-optimization\u002Fhls-streaming-vs-progressive-mp4\u002Findex.md","HLS Streaming vs Progressive MP4",{"type":7,"value":8,"toc":1008},"minimark",[9,14,43,46,159,164,200,204,210,216,222,228,302,306,311,318,322,585,588,592,598,602,669,803,807,810,814,817,821,824,828,858,862,868,874,880,886,890,902,911,924,933,942,957,966,970,993,998,1001,1004],[10,11,13],"h1",{"id":12},"hls-streaming-vs-progressive-mp4-choosing-a-delivery-format","HLS Streaming vs Progressive MP4: Choosing a Delivery Format",[15,16,17,18,23,24,28,29,33,34,38,39,42],"p",{},"This comparison belongs to ",[19,20,22],"a",{"href":21},"\u002Fimage-media-optimization\u002Fvideo-performance-optimization\u002F","Video Performance Optimization"," within ",[19,25,27],{"href":26},"\u002Fimage-media-optimization\u002F","Image & Media Optimization",". There are two fundamentally different ways to deliver video on the web. ",[30,31,32],"strong",{},"Progressive download"," serves a single file (MP4 or WebM) that the browser fetches with range requests and plays natively in ",[35,36,37],"code",{},"\u003Cvideo>",". ",[30,40,41],{},"Adaptive streaming"," (HLS or DASH) splits the video into short segments at several bitrates; a player picks segments based on measured bandwidth, switching quality as conditions change.",[15,44,45],{},"Each has a performance profile. Progressive files start quickly, need no player JavaScript, and cache trivially — but every viewer gets the same bitrate, so slow connections buffer and fast ones are underserved. Adaptive streaming handles variable networks gracefully and avoids downloading far more than the viewer watches, but requires a manifest round trip before playback and, outside Safari, a JavaScript player (hls.js, Shaka, dash.js) of tens to hundreds of kilobytes that runs on the main thread.",[15,47,48],{},[49,50,56,57,56,64,56,68,56,71,56,80,56,86,56,95,56,101,56,106,56,111,56,114,56,117,56,120,56,123,56,126,56,129,56,134,56,138,56,140,56,144,56,146,56,149,56,151,56,154,56,156,56],"svg",{"viewBox":51,"width":52,"role":53,"ariaLabel":54,"style":55},"0 0 760 228","100%","img","Comparison of progressive MP4 delivery and HLS adaptive streaming by startup, player cost, network adaptation and caching.","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)"," ",[58,59],"rect",{"className":60,"x":62,"y":62,"width":52,"height":52,"fill":63},[61],"svg-canvas","0","#ffffff",[65,66,67],"title",{},"Progressive MP4 vs HLS",[69,70,54],"desc",{},[58,72],{"x":73,"y":73,"width":74,"height":75,"rx":76,"fill":77,"stroke":78,"style":79},"1","758","226","10","none","currentColor","stroke-opacity:0.18",[81,82,67],"text",{"x":83,"y":84,"fill":78,"style":85},"28.0","34.0","font-size:16px;font-weight:700",[58,87],{"x":83,"y":88,"width":89,"height":90,"rx":91,"fill":92,"stroke":93,"style":94},"56.0","340.0","150.0","6","#ffc300","#b8860b","fill-opacity:0.24;stroke-opacity:0.9",[81,96,100],{"x":97,"y":98,"fill":78,"style":99},"42.0","82.0","font-size:14px;font-weight:700","Progressive MP4",[81,102,105],{"x":97,"y":103,"fill":78,"style":104},"108.0","font-size:12.5px;font-weight:700","•",[81,107,110],{"x":88,"y":103,"fill":78,"style":108,"textAnchor":109},"font-size:12.5px","start","One file, native playback",[81,112,105],{"x":97,"y":113,"fill":78,"style":104},"132.0",[81,115,116],{"x":88,"y":113,"fill":78,"style":108,"textAnchor":109},"No player JS",[81,118,105],{"x":97,"y":119,"fill":78,"style":104},"156.0",[81,121,122],{"x":88,"y":119,"fill":78,"style":108,"textAnchor":109},"Fixed bitrate for everyone",[81,124,105],{"x":97,"y":125,"fill":78,"style":104},"180.0",[81,127,128],{"x":88,"y":125,"fill":78,"style":108,"textAnchor":109},"Best for clips under ~1 minute",[58,130],{"x":131,"y":88,"width":89,"height":90,"rx":91,"fill":132,"stroke":132,"style":133},"392.0","#0466c8","fill-opacity:0.14;stroke-opacity:0.9",[81,135,137],{"x":136,"y":98,"fill":78,"style":99},"406.0","HLS adaptive streaming",[81,139,105],{"x":136,"y":103,"fill":78,"style":104},[81,141,143],{"x":142,"y":103,"fill":78,"style":108,"textAnchor":109},"420.0","Manifest + segments at several bitrates",[81,145,105],{"x":136,"y":113,"fill":78,"style":104},[81,147,148],{"x":142,"y":113,"fill":78,"style":108,"textAnchor":109},"Player JS outside Safari (hls.js)",[81,150,105],{"x":136,"y":119,"fill":78,"style":104},[81,152,153],{"x":142,"y":119,"fill":78,"style":108,"textAnchor":109},"Adapts to bandwidth, less rebuffering",[81,155,105],{"x":136,"y":125,"fill":78,"style":104},[81,157,158],{"x":142,"y":125,"fill":78,"style":108,"textAnchor":109},"Best for long-form and live content",[160,161,163],"h2",{"id":162},"rapid-diagnosis","Rapid Diagnosis",[165,166,167,174,184,190],"ul",{},[168,169,170,173],"li",{},[30,171,172],{},"Classify your video by length and audience network."," Short loops and clips under a minute rarely need adaptive streaming; long tutorials, courses and live streams usually do.",[168,175,176,179,180,183],{},[30,177,178],{},"Measure startup."," Time from play intent to the first frame (",[35,181,182],{},"playing"," event); HLS adds manifest and initial segment fetches.",[168,185,186,189],{},[30,187,188],{},"Measure player cost."," In a trace, look for the streaming library's script evaluation and long tasks around player initialisation.",[168,191,192,195,196,199],{},[30,193,194],{},"Measure rebuffering."," Count ",[35,197,198],{},"waiting"," events during playback in RUM, segmented by connection type.",[160,201,203],{"id":202},"root-cause-analysis","Root Cause Analysis",[15,205,206,209],{},[30,207,208],{},"1. Adaptive streaming for short clips."," Loading hls.js and manifests for a 15-second loop adds JavaScript and round trips for no benefit.",[15,211,212,215],{},[30,213,214],{},"2. Progressive files for long content on mobile."," A single 1080p file stalls on weaker connections and wastes bytes when viewers stop early.",[15,217,218,221],{},[30,219,220],{},"3. Player initialised at load."," Streaming players set up on page load, even for videos below the fold or never played, adding main-thread work.",[15,223,224,227],{},[30,225,226],{},"4. Segment caching misconfigured."," Manifests cached too long (live) or segments not cached at all (VOD) degrade both freshness and performance.",[15,229,230],{},[49,231,56,234,56,237,56,240,56,242,56,245,56,247,56,254,56,261,56,266,56,270,56,274,56,278,56,281,56,286,56,290,56,294,56,298,56],{"viewBox":232,"width":52,"role":53,"ariaLabel":233,"style":55},"0 0 760 194","Bar chart comparing time to first frame for progressive MP4 and HLS with native and JavaScript players.",[58,235],{"className":236,"x":62,"y":62,"width":52,"height":52,"fill":63},[61],[65,238,239],{},"Time to first frame on a 4G connection (2-minute video)",[69,241,233],{},[58,243],{"x":73,"y":73,"width":74,"height":244,"rx":76,"fill":77,"stroke":78,"style":79},"192",[81,246,239],{"x":83,"y":84,"fill":78,"style":85},[81,248,253],{"x":249,"y":250,"fill":78,"style":251,"textAnchor":252},"194.0","70.0","font-size:13px","end","Progressive MP4 (faststart)",[58,255],{"x":256,"y":88,"width":257,"height":258,"rx":259,"fill":132,"stroke":132,"style":260},"206.0","231.0","19","3","fill-opacity:0.55;stroke-opacity:0.9",[81,262,265],{"x":263,"y":250,"fill":78,"style":264},"443.0","font-size:12px;font-weight:600","620ms",[81,267,269],{"x":249,"y":268,"fill":78,"style":251,"textAnchor":252},"101.0","HLS native (Safari)",[58,271],{"x":256,"y":272,"width":273,"height":258,"rx":259,"fill":132,"stroke":132,"style":260},"87.0","290.6",[81,275,277],{"x":276,"y":268,"fill":78,"style":264},"502.6","780ms",[81,279,280],{"x":249,"y":113,"fill":78,"style":251,"textAnchor":252},"HLS via hls.js (cold)",[58,282],{"x":256,"y":283,"width":284,"height":258,"rx":259,"fill":92,"stroke":93,"style":285},"118.0","462.0","fill-opacity:0.7;stroke-opacity:0.9",[81,287,289],{"x":288,"y":113,"fill":78,"style":264},"674.0","1240ms",[81,291,293],{"x":249,"y":292,"fill":78,"style":251,"textAnchor":252},"163.0","HLS via hls.js (preloaded lib)",[58,295],{"x":256,"y":296,"width":297,"height":258,"rx":259,"fill":132,"stroke":132,"style":260},"149.0","320.4",[81,299,301],{"x":300,"y":292,"fill":78,"style":264},"532.4","860ms",[160,303,305],{"id":304},"step-by-step-choosing-and-implementing","Step-by-Step: Choosing and Implementing",[307,308,310],"h3",{"id":309},"_1-use-progressive-files-for-short-content","1. Use progressive files for short content",[15,312,313,314,317],{},"For loops, product clips and short explainers, serve modern-codec progressive files with an H.264 fallback, ",[35,315,316],{},"faststart"," MP4s and a good poster. No player library is needed.",[307,319,321],{"id":320},"_2-use-hls-for-long-form-and-variable-networks-loaded-on-intent","2. Use HLS for long-form and variable networks, loaded on intent",[323,324,329],"pre",{"className":325,"code":326,"language":327,"meta":328,"style":328},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","const video = document.querySelector('#course-video');\nvideo.addEventListener('click', async () => {\n  if (video.canPlayType('application\u002Fvnd.apple.mpegurl')) {\n    video.src = '\u002Fstream\u002Flesson-12\u002Fmaster.m3u8';          \u002F\u002F native HLS (Safari, iOS)\n  } else {\n    const { default: Hls } = await import('hls.js\u002Fdist\u002Fhls.light.mjs');   \u002F\u002F load player only on intent\n    const hls = new Hls({ capLevelToPlayerSize: true, startLevel: -1 });\n    hls.loadSource('\u002Fstream\u002Flesson-12\u002Fmaster.m3u8');\n    hls.attachMedia(video);\n  }\n  video.play();\n}, { once: true });\n\u002F\u002F trade-off: loading the player on click adds its download to time-to-first-\n\u002F\u002F frame. Prefetch the library on hover or when the video enters the viewport.\n","javascript","",[35,330,331,365,394,414,433,444,485,518,534,545,551,563,573,579],{"__ignoreMap":328},[332,333,336,340,344,347,351,355,358,362],"span",{"class":334,"line":335},"line",1,[332,337,339],{"class":338},"sPARh","const",[332,341,343],{"class":342},"sPXB4"," video",[332,345,346],{"class":338}," =",[332,348,350],{"class":349},"saISM"," document.",[332,352,354],{"class":353},"smZ65","querySelector",[332,356,357],{"class":349},"(",[332,359,361],{"class":360},"sZ8jY","'#course-video'",[332,363,364],{"class":349},");\n",[332,366,368,371,374,376,379,382,385,388,391],{"class":334,"line":367},2,[332,369,370],{"class":349},"video.",[332,372,373],{"class":353},"addEventListener",[332,375,357],{"class":349},[332,377,378],{"class":360},"'click'",[332,380,381],{"class":349},", ",[332,383,384],{"class":338},"async",[332,386,387],{"class":349}," () ",[332,389,390],{"class":338},"=>",[332,392,393],{"class":349}," {\n",[332,395,397,400,403,406,408,411],{"class":334,"line":396},3,[332,398,399],{"class":338},"  if",[332,401,402],{"class":349}," (video.",[332,404,405],{"class":353},"canPlayType",[332,407,357],{"class":349},[332,409,410],{"class":360},"'application\u002Fvnd.apple.mpegurl'",[332,412,413],{"class":349},")) {\n",[332,415,417,420,423,426,429],{"class":334,"line":416},4,[332,418,419],{"class":349},"    video.src ",[332,421,422],{"class":338},"=",[332,424,425],{"class":360}," '\u002Fstream\u002Flesson-12\u002Fmaster.m3u8'",[332,427,428],{"class":349},";          ",[332,430,432],{"class":431},"sjfSM","\u002F\u002F native HLS (Safari, iOS)\n",[332,434,436,439,442],{"class":334,"line":435},5,[332,437,438],{"class":349},"  } ",[332,440,441],{"class":338},"else",[332,443,393],{"class":349},[332,445,447,450,453,457,460,463,466,468,471,474,476,479,482],{"class":334,"line":446},6,[332,448,449],{"class":338},"    const",[332,451,452],{"class":349}," { ",[332,454,456],{"class":455},"sQw3B","default",[332,458,459],{"class":349},": ",[332,461,462],{"class":342},"Hls",[332,464,465],{"class":349}," } ",[332,467,422],{"class":338},[332,469,470],{"class":338}," await",[332,472,473],{"class":338}," import",[332,475,357],{"class":349},[332,477,478],{"class":360},"'hls.js\u002Fdist\u002Fhls.light.mjs'",[332,480,481],{"class":349},");   ",[332,483,484],{"class":431},"\u002F\u002F load player only on intent\n",[332,486,488,490,493,495,498,501,504,507,510,513,515],{"class":334,"line":487},7,[332,489,449],{"class":338},[332,491,492],{"class":342}," hls",[332,494,346],{"class":338},[332,496,497],{"class":338}," new",[332,499,500],{"class":353}," Hls",[332,502,503],{"class":349},"({ capLevelToPlayerSize: ",[332,505,506],{"class":342},"true",[332,508,509],{"class":349},", startLevel: ",[332,511,512],{"class":338},"-",[332,514,73],{"class":342},[332,516,517],{"class":349}," });\n",[332,519,521,524,527,529,532],{"class":334,"line":520},8,[332,522,523],{"class":349},"    hls.",[332,525,526],{"class":353},"loadSource",[332,528,357],{"class":349},[332,530,531],{"class":360},"'\u002Fstream\u002Flesson-12\u002Fmaster.m3u8'",[332,533,364],{"class":349},[332,535,537,539,542],{"class":334,"line":536},9,[332,538,523],{"class":349},[332,540,541],{"class":353},"attachMedia",[332,543,544],{"class":349},"(video);\n",[332,546,548],{"class":334,"line":547},10,[332,549,550],{"class":349},"  }\n",[332,552,554,557,560],{"class":334,"line":553},11,[332,555,556],{"class":349},"  video.",[332,558,559],{"class":353},"play",[332,561,562],{"class":349},"();\n",[332,564,566,569,571],{"class":334,"line":565},12,[332,567,568],{"class":349},"}, { once: ",[332,570,506],{"class":342},[332,572,517],{"class":349},[332,574,576],{"class":334,"line":575},13,[332,577,578],{"class":431},"\u002F\u002F trade-off: loading the player on click adds its download to time-to-first-\n",[332,580,582],{"class":334,"line":581},14,[332,583,584],{"class":431},"\u002F\u002F frame. Prefetch the library on hover or when the video enters the viewport.\n",[15,586,587],{},"Expected outcome: no streaming JavaScript on pages where users never play the video.",[307,589,591],{"id":590},"_3-cap-renditions-to-the-player-size","3. Cap renditions to the player size",[15,593,594,597],{},[35,595,596],{},"capLevelToPlayerSize"," (hls.js) or equivalent prevents downloading 1080p segments into a 360px player, saving bandwidth and decode work on mobile.",[307,599,601],{"id":600},"_4-cache-segments-immutably-and-manifests-briefly","4. Cache segments immutably and manifests briefly",[323,603,607],{"className":604,"code":605,"language":606,"meta":328,"style":328},"language-nginx shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","location ~* \\.(m4s|ts|mp4)$ { add_header Cache-Control \"public, max-age=31536000, immutable\"; }\nlocation ~* \\.m3u8$       { add_header Cache-Control \"public, max-age=2\"; }   # live; longer for VOD\n# trade-off: for live streams, manifest TTL bounds latency; for VOD, manifests\n# never change and can be cached long.\n","nginx",[35,608,609,635,659,664],{"__ignoreMap":328},[332,610,611,614,617,620,623,626,629,632],{"class":334,"line":335},[332,612,613],{"class":338},"location",[332,615,616],{"class":338}," ~*",[332,618,619],{"class":360}," \\.(m4s|ts|mp4)$ ",[332,621,622],{"class":349},"{",[332,624,625],{"class":338}," add_header ",[332,627,628],{"class":349},"Cache-Control ",[332,630,631],{"class":360},"\"public, max-age=31536000, immutable\"",[332,633,634],{"class":349},"; }\n",[332,636,637,639,641,644,646,648,650,653,656],{"class":334,"line":367},[332,638,613],{"class":338},[332,640,616],{"class":338},[332,642,643],{"class":360}," \\.m3u8$       ",[332,645,622],{"class":349},[332,647,625],{"class":338},[332,649,628],{"class":349},[332,651,652],{"class":360},"\"public, max-age=2\"",[332,654,655],{"class":349},"; }   ",[332,657,658],{"class":431},"# live; longer for VOD\n",[332,660,661],{"class":334,"line":396},[332,662,663],{"class":431},"# trade-off: for live streams, manifest TTL bounds latency; for VOD, manifests\n",[332,665,666],{"class":334,"line":416},[332,667,668],{"class":431},"# never change and can be cached long.\n",[15,670,671],{},[49,672,56,675,56,678,56,681,56,683,56,700,56,703,56,705,56,710,56,717,56,720,56,724,56,732,56,738,56,742,56,748,56,751,56,754,56,756,56,759,56,762,56,765,56,768,56,771,56,774,56,778,56,780,56,783,56,786,56,789,56,793,56,796,56,799,56],{"viewBox":673,"width":52,"role":53,"ariaLabel":674,"style":55},"0 0 760 325","Decision sequence for choosing progressive download or adaptive streaming for a video.",[58,676],{"className":677,"x":62,"y":62,"width":52,"height":52,"fill":63},[61],[65,679,680],{},"Progressive or adaptive?",[69,682,674],{},[684,685,686],"defs",{},[687,688,695],"marker",{"id":689,"viewBox":690,"refX":691,"refY":692,"markerWidth":693,"markerHeight":693,"orient":694},"fa85e8ca30","0 0 10 10","9","5","7","auto-start-reverse",[696,697],"path",{"d":698,"fill":78,"style":699},"M0 0 L10 5 L0 10 z","fill-opacity:0.7",[58,701],{"x":73,"y":73,"width":74,"height":702,"rx":76,"fill":77,"stroke":78,"style":79},"323",[81,704,680],{"x":83,"y":84,"fill":78,"style":85},[58,706],{"x":83,"y":88,"width":707,"height":708,"rx":91,"fill":78,"stroke":78,"style":709},"320.0","35.0","fill-opacity:0.06;stroke-opacity:0.4",[81,711,716],{"x":712,"y":713,"fill":78,"style":714,"textAnchor":715},"188.0","78.0","font-size:13px;font-weight:700","middle","Live stream?",[58,718],{"x":719,"y":88,"width":707,"height":708,"rx":91,"fill":132,"stroke":132,"style":133},"412.0",[81,721,723],{"x":722,"y":713,"fill":78,"style":108,"textAnchor":715},"572.0","HLS\u002FDASH (adaptive streaming required)",[334,725],{"x1":726,"y1":727,"x2":728,"y2":727,"stroke":78,"strokeWidth":729,"style":730,"markerEnd":731},"348.0","73.5","410.0","1.5","stroke-opacity:0.6","url(#fa85e8ca30)",[81,733,737],{"x":734,"y":735,"fill":132,"style":736,"textAnchor":715},"380.0","66.5","font-size:11.5px;font-weight:700","yes",[334,739],{"x1":712,"y1":740,"x2":712,"y2":741,"stroke":78,"strokeWidth":729,"style":730,"markerEnd":731},"91.0","125.0",[81,743,747],{"x":744,"y":745,"fill":746,"style":736},"196.0","113.0","#51617a","no",[58,749],{"x":83,"y":750,"width":707,"height":708,"rx":91,"fill":78,"stroke":78,"style":709},"127.0",[81,752,753],{"x":712,"y":296,"fill":78,"style":714,"textAnchor":715},"Longer than ~1-2 minutes?",[58,755],{"x":719,"y":750,"width":707,"height":708,"rx":91,"fill":132,"stroke":132,"style":133},[81,757,758],{"x":722,"y":296,"fill":78,"style":108,"textAnchor":715},"HLS with the player loaded on intent",[334,760],{"x1":726,"y1":761,"x2":728,"y2":761,"stroke":78,"strokeWidth":729,"style":730,"markerEnd":731},"144.5",[81,763,737],{"x":734,"y":764,"fill":132,"style":736,"textAnchor":715},"137.5",[334,766],{"x1":712,"y1":767,"x2":712,"y2":744,"stroke":78,"strokeWidth":729,"style":730,"markerEnd":731},"162.0",[81,769,747],{"x":744,"y":770,"fill":746,"style":736},"184.0",[58,772],{"x":83,"y":773,"width":707,"height":708,"rx":91,"fill":78,"stroke":78,"style":709},"198.0",[81,775,777],{"x":712,"y":776,"fill":78,"style":714,"textAnchor":715},"220.0","Audience on highly variable mobile networks?",[58,779],{"x":719,"y":773,"width":707,"height":708,"rx":91,"fill":132,"stroke":132,"style":133},[81,781,782],{"x":722,"y":776,"fill":78,"style":108,"textAnchor":715},"HLS even for medium-length clips",[334,784],{"x1":726,"y1":785,"x2":728,"y2":785,"stroke":78,"strokeWidth":729,"style":730,"markerEnd":731},"215.5",[81,787,737],{"x":734,"y":788,"fill":132,"style":736,"textAnchor":715},"208.5",[334,790],{"x1":712,"y1":791,"x2":712,"y2":792,"stroke":78,"strokeWidth":729,"style":730,"markerEnd":731},"233.0","267.0",[81,794,747],{"x":744,"y":795,"fill":746,"style":736},"255.0",[58,797],{"x":83,"y":798,"width":707,"height":84,"rx":91,"fill":92,"stroke":93,"style":94},"269.0",[81,800,802],{"x":712,"y":801,"fill":78,"style":108,"textAnchor":715},"290.5","Progressive MP4\u002FWebM with a good poster",[160,804,806],{"id":805},"verification","Verification",[15,808,809],{},"Measure time to first frame, rebuffering events and bytes downloaded per view for each format on representative content and networks. Confirm the streaming player loads only when needed (no player requests in the Network panel on page load). In RUM, track rebuffer ratio and average bitrate by connection type to confirm adaptive streaming is adapting.",[160,811,813],{"id":812},"worked-example-an-online-course-platform","Worked Example: An Online Course Platform",[15,815,816],{},"A course platform served 20–40 minute lessons as single 1080p MP4 files. Mobile learners on weaker connections experienced frequent stalls, and data usage complaints were common because learners often stopped after a few minutes while the browser had buffered far ahead. Switching lessons to HLS with five renditions, loading hls.js only when the play button was pressed (prefetched on hover), and capping levels to player size reduced rebuffering events by 70%, cut average bytes per session by 45%, and left LCP unaffected because the page's LCP was the lesson thumbnail.",[160,818,820],{"id":819},"the-hidden-cost-of-player-libraries","The Hidden Cost of Player Libraries",[15,822,823],{},"Streaming players do real work on the main thread: parsing manifests, managing buffers, running adaptation logic and handling events. On low-end devices this can produce long tasks during startup and occasionally during playback. Choose lightweight builds (hls.js offers a \"light\" build without alternate audio and subtitle features), initialise players only when playback is requested, and avoid wrapping them in heavy UI frameworks for controls. Native HLS in Safari has no JavaScript cost, and Chromium is gaining native HLS support in some contexts; feature-detect and prefer native playback where available.",[160,825,827],{"id":826},"common-mistakes","Common Mistakes",[165,829,830,836,842,848],{},[168,831,832,835],{},[30,833,834],{},"Streaming tiny clips."," Adds latency and JavaScript for no benefit.",[168,837,838,841],{},[30,839,840],{},"Initialising players at page load."," Costs every visitor main-thread time.",[168,843,844,847],{},[30,845,846],{},"No poster for streamed video."," Players take longer to show the first frame; a poster covers the gap.",[168,849,850,853,854,857],{},[30,851,852],{},"Non-faststart MP4s."," Progressive playback cannot start until the ",[35,855,856],{},"moov"," atom arrives.",[160,859,861],{"id":860},"edge-cases","Edge Cases",[15,863,864,867],{},[30,865,866],{},"DRM."," Protected content requires EME and adaptive streaming; budget for license requests in startup time.",[15,869,870,873],{},[30,871,872],{},"Low-latency live."," LL-HLS uses partial segments and blocking playlist reloads; CDN support and short manifest TTLs are essential.",[15,875,876,879],{},[30,877,878],{},"Offline downloads."," Progressive files are easier to download for offline viewing; HLS requires packaging support.",[15,881,882,885],{},[30,883,884],{},"Analytics."," Player analytics plugins add more JavaScript; load them with the player, not at page load.",[160,887,889],{"id":888},"faq","FAQ",[891,892,895,899],"details",{"className":893},[894],"faq-item",[896,897,898],"summary",{},"Is DASH better than HLS?",[15,900,901],{},"They are functionally similar. HLS plays natively in Safari and iOS, which makes it the pragmatic default; DASH requires a JavaScript player everywhere. Many platforms package both from the same CMAF segments.",[891,903,905,908],{"className":904},[894],[896,906,907],{},"Does adaptive streaming improve Core Web Vitals?",[15,909,910],{},"Not directly — playback quality is outside Core Web Vitals. Done carelessly (player loaded at startup) it can hurt INP and LCP; done well (loaded on intent) it is neutral for vitals and much better for playback.",[891,912,914,917],{"className":913},[894],[896,915,916],{},"Can progressive files adapt to the device?",[15,918,919,920,923],{},"Partially: ",[35,921,922],{},"\u003Csource media>"," can pick a rendition by viewport size, and multiple codecs by type. They cannot adapt to bandwidth during playback.",[891,925,927,930],{"className":926},[894],[896,928,929],{},"What segment length should I use?",[15,931,932],{},"Two to six seconds is typical. Shorter segments adapt faster and start sooner but add request overhead; longer segments compress slightly better.",[891,934,936,939],{"className":935},[894],[896,937,938],{},"Should video segments go through the main site CDN?",[15,940,941],{},"Yes, ideally on the same or a well-connected CDN with immutable caching and range-request support. Separate video hostnames add connection setup, which a preconnect on play intent can hide.",[891,943,945,948],{"className":944},[894],[896,946,947],{},"How do I measure rebuffering?",[15,949,950,951,953,954,956],{},"Listen for ",[35,952,198],{}," and ",[35,955,182],{}," events, sum the time between them during playback, and divide by watch time to get a rebuffer ratio. Report it per format and connection type.",[891,958,960,963],{"className":959},[894],[896,961,962],{},"Can I start with progressive files and add HLS later?",[15,964,965],{},"Yes. Keep the poster and container markup independent of the delivery format, and wrap source selection in one component. Switching a template from progressive to HLS then changes only that component, and you can compare startup and rebuffering metrics before and after on the same pages.",[160,967,969],{"id":968},"related","Related",[165,971,972,979,986],{},[168,973,974,978],{},[19,975,977],{"href":976},"\u002Fimage-media-optimization\u002Fvideo-performance-optimization\u002Fvideo-poster-images-and-lcp\u002F","Video poster images and LCP"," — covering player startup with a poster.",[168,980,981,985],{},[19,982,984],{"href":983},"\u002Fimage-media-optimization\u002Fvideo-performance-optimization\u002Flazy-loading-video-without-hurting-lcp\u002F","Lazy-loading video without hurting LCP"," — deferring players and data.",[168,987,988,992],{},[19,989,991],{"href":990},"\u002Fnetwork-protocol-optimization\u002Fhttp2-http3-and-connection-management\u002Fmeasuring-real-world-http3-gains\u002F","Measuring real-world HTTP\u002F3 gains"," — transport improvements that help segment delivery.",[994,995,997],"script",{"type":996},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"HLS Streaming vs Progressive MP4: Choosing a Delivery Format\",\n  \"description\": \"When to serve video as a single progressive file and when adaptive streaming earns its player cost, with the performance trade-offs of each.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Use progressive files for short content\",\n      \"text\": \"For loops, product clips and short explainers, serve modern-codec progressive files with an H.264 fallback, faststart MP4s and a good poster.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Use HLS for long-form and variable networks, loaded on intent\",\n      \"text\": \"Expected outcome: no streaming JavaScript on pages where users never play the video.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Cap renditions to the player size\",\n      \"text\": \"capLevelToPlayerSize (hls.js) or equivalent prevents downloading 1080p segments into a 360px player, saving bandwidth and decode work on mobile.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Cache segments immutably and manifests briefly\",\n      \"text\": \"Cache segments immutably and manifests briefly\"\n    }\n  ]\n}\n",[994,999,1000],{"type":996},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"HLS Streaming vs Progressive MP4: Choosing a Delivery Format\",\n  \"description\": \"When to serve video as a single progressive file and when adaptive streaming earns its player cost, with the performance trade-offs of each.\",\n  \"datePublished\": \"2026-10-06\",\n  \"dateModified\": \"2026-10-06\",\n  \"author\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"publisher\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"mainEntityOfPage\": {\n    \"@type\": \"WebPage\",\n    \"@id\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Fvideo-performance-optimization\u002Fhls-streaming-vs-progressive-mp4\u002F\"\n  }\n}\n",[994,1002,1003],{"type":996},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 1,\n      \"name\": \"Home\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 2,\n      \"name\": \"Image & Media Optimization\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 3,\n      \"name\": \"Video Performance Optimization\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Fvideo-performance-optimization\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 4,\n      \"name\": \"HLS Streaming vs Progressive MP4\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fimage-media-optimization\u002Fvideo-performance-optimization\u002Fhls-streaming-vs-progressive-mp4\u002F\"\n    }\n  ]\n}\n",[1005,1006,1007],"style",{},"html pre.shiki code .sPARh, html code.shiki .sPARh{--shiki-default:#A0111F;--shiki-dark:#FF9492;--shiki-light:#A0111F}html pre.shiki code .sPXB4, html code.shiki .sPXB4{--shiki-default:#023B95;--shiki-dark:#91CBFF;--shiki-light:#023B95}html pre.shiki code .saISM, html code.shiki .saISM{--shiki-default:#0E1116;--shiki-dark:#F0F3F6;--shiki-light:#0E1116}html pre.shiki code .smZ65, html code.shiki .smZ65{--shiki-default:#622CBC;--shiki-dark:#DBB7FF;--shiki-light:#622CBC}html pre.shiki code .sZ8jY, html code.shiki .sZ8jY{--shiki-default:#032563;--shiki-dark:#ADDCFF;--shiki-light:#032563}html pre.shiki code .sjfSM, html code.shiki .sjfSM{--shiki-default:#66707B;--shiki-dark:#BDC4CC;--shiki-light:#66707B}html pre.shiki code .sQw3B, html code.shiki .sQw3B{--shiki-default:#702C00;--shiki-dark:#FFB757;--shiki-light:#702C00}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}",{"title":328,"searchDepth":367,"depth":367,"links":1009},[1010,1011,1012,1018,1019,1020,1021,1022,1023,1024],{"id":162,"depth":367,"text":163},{"id":202,"depth":367,"text":203},{"id":304,"depth":367,"text":305,"children":1013},[1014,1015,1016,1017],{"id":309,"depth":396,"text":310},{"id":320,"depth":396,"text":321},{"id":590,"depth":396,"text":591},{"id":600,"depth":396,"text":601},{"id":805,"depth":367,"text":806},{"id":812,"depth":367,"text":813},{"id":819,"depth":367,"text":820},{"id":826,"depth":367,"text":827},{"id":860,"depth":367,"text":861},{"id":888,"depth":367,"text":889},{"id":968,"depth":367,"text":969},"When to serve video as a single progressive file and when adaptive streaming earns its player cost, with the performance trade-offs of each.","md",{"slug":1028,"type":1029,"breadcrumb":1030,"datePublished":1038,"dateModified":1038},"hls-streaming-vs-progressive-mp4","article",[1031,1034,1035,1036],{"name":1032,"url":1033},"Home","\u002F",{"name":27,"url":26},{"name":22,"url":21},{"name":5,"url":1037},"\u002Fimage-media-optimization\u002Fvideo-performance-optimization\u002Fhls-streaming-vs-progressive-mp4\u002F","2026-10-06",true,"\u002Fimage-media-optimization\u002Fvideo-performance-optimization\u002Fhls-streaming-vs-progressive-mp4",{"title":1042,"description":1043},"HLS Streaming vs Progressive MP4 for Web Video","Progressive MP4 is simple and fast to start for short clips; HLS adapts bitrate for long or variable-network playback. Compare startup, bytes and player cost.","image-media-optimization\u002Fvideo-performance-optimization\u002Fhls-streaming-vs-progressive-mp4\u002Findex","gs4qEZZZUMInWW3oIxAt_3fsGLozLisBN53uO-H8VQo",[1047,1050],{"title":22,"path":1048,"stem":1049,"children":-1},"\u002Fimage-media-optimization\u002Fvideo-performance-optimization","image-media-optimization\u002Fvideo-performance-optimization\u002Findex",{"title":1051,"path":1052,"stem":1053,"children":-1},"Lazy-Loading Video Without Hurting LCP","\u002Fimage-media-optimization\u002Fvideo-performance-optimization\u002Flazy-loading-video-without-hurting-lcp","image-media-optimization\u002Fvideo-performance-optimization\u002Flazy-loading-video-without-hurting-lcp\u002Findex",1791308077313]