Check whether hls.js is actually using a worker (most ESM setups aren't)
TL;DR If you import Hls from 'hls.js' , you are probably getting the ESM build, and the ESM build does not bundle the transmuxer worker. Transmuxing runs on your main thread until you set workerPath . We are going to verify which mode you are in, fix it, and add a long-task observer so you can tell main-thread stalls apart from network stalls. Two short facts before any code. hls.js 1.4 introduced the ESM build ( dist/hls.mjs ), and that build ships the worker as a separate file rather than inlining it. And Chromium has supported MediaSource inside dedicated workers since Chrome 108 , which is a different thing that we will get to at the end. Everything here was checked against hls.js 1.7.x . 1. Find out what you are currently running 🔎 Do not guess. Run this in the console on a page where a video is playing: // paste in DevTools console during playback performance . getEntriesByType ( ' resource ' ) . filter ( e => e . name . includes ( ' worker ' )) . map ( e => e . name ); Empty array means no worker file was ever fetched. Then check DevTools → Sources → Threads (Chrome) or the Debugger's worker list (Firefox). If the only thread listed is the main one, hls.js is transmuxing inline. You can also ask the library directly: // after new Hls(...) console . log ( hls . config . workerPath ); // null if you never set it console . log ( hls . config . enableWorker ); // true by default, which is misleading ⚠️ Note: enableWorker: true is the default and it stays true even when no worker can be created. It means "use a worker if one is available", not "a worker is running". This is the single biggest source of false confidence here. 2. Wire up workerPath 🛠️ The pattern is: get a real URL for hls.js/dist/hls.worker.js , pass it as workerPath . The syntax differs per bundler. Vite / Rollup: // src/player.ts import Hls from ' hls.js ' ; import workerUrl from ' hls.js/dist/hls.worker.js?url ' ; const hls = new Hls ({ workerPath : workerUrl , }); webpack 5: // src/player.js im