Media elements (Multimedia) report buffered (downloaded), seekable and played spans as TimeRanges: length plus start(i) and end(i) in seconds. Ranges are ordered and merged, so a gap appears as a second entry.
const video = Object.assign(document.createElement('video'),
{ muted: true, src: 'https://mdn.github.io/shared-assets/videos/flower.webm' });
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const show = (r) => Array.from({ length: r.length },
(_, i) => `${r.start(i).toFixed(1)}-${r.end(i).toFixed(1)}`).join(', ');
video.addEventListener('loadedmetadata', async () => {
video.currentTime = 1; await video.play(); await sleep(1000); // play from 1.0 s
video.currentTime = 3.5; await sleep(500); video.pause(); // skip ahead
for (const name of ['buffered', 'seekable', 'played']) {
console.log(`${name}: ${show(video[name])}`);
}
});Output
buffered: 0.0-5.1 seekable: 0.0-5.1 played: 1.0-2.0, 3.5-3.8
The small file downloaded completely, so buffered holds one range. Re-read it on each progress event. start() and end() throw an IndexSizeError for an out-of-range index, such as end(0) before any data arrives, so check length first.