Stream Video Downloader logo
← Back to Blog

The MediaRecorder API Explained: How Browsers Capture Audio and Video Natively

August 16, 2026 · 6 min read

Key takeaways

  • MediaRecorder is a built-in browser API that records live MediaStream tracks — from a tab, window, microphone, or webcam — directly to a compressed container without any third-party library.
  • The output format is controlled by the mimeType option; VP8/VP9 in WebM is the widest default, while H.264 in MP4 is available in most browsers when hardware support is present.
  • Data arrives in chunks via the ondataavailable event; the recording is only usable as a complete file once the stop event fires and all chunks are concatenated into a Blob.
  • MediaRecorder captures a live stream, not a playlist — it cannot be used to download HLS or DASH video, which arrives as pre-encoded segments rather than a capturable MediaStream.

Every browser-based screen recorder — the kind that captures a tab or window and produces a video file without installing anything — rests on one Web API: MediaRecorder. It is the capture half of the browser's media pipeline: where getDisplayMedia and getUserMedia produce a live stream of pixels and samples, MediaRecorder consumes that stream and writes it to a file.

What a MediaStream actually is

Before MediaRecorder makes sense, it helps to understand what it records. A MediaStream is a collection of MediaStreamTrack objects — each one a live source of audio or video data. Tracks come from three places:

  • navigator.mediaDevices.getUserMedia() — microphone audio and/or webcam video.
  • navigator.mediaDevices.getDisplayMedia() — a browser tab, application window, or entire screen, plus optionally the system audio playing through that surface.
  • A <canvas> element via canvas.captureStream() — a live video track from whatever is drawn on the canvas at the given frame rate.

Tracks from different sources can be combined into one stream by creating a new MediaStream and adding tracks to it — a common pattern for recording screen video alongside microphone audio when getDisplayMedia cannot capture the mic in the same call.

The MediaRecorder lifecycle

Constructing a MediaRecorder attaches it to a stream but does not start recording. The lifecycle has four distinct states:

  1. inactive — after construction, or after stop() completes.
  2. recording — after start() is called. The recorder is actively consuming tracks and encoding data.
  3. paused — after pause(). Encoding is suspended; the stream continues but no data is written.
  4. recording again — after resume(), back from paused.

The start(timeslice?) method accepts an optional timeslice in milliseconds. When provided, the recorder fires ondataavailable with a chunk of encoded data every timeslice milliseconds rather than waiting until stop. This is how streaming upload or preview-while-recording scenarios work — data arrives progressively rather than all at once at the end.

The ondataavailable event and Blob assembly

Recorded data arrives through the ondataavailable event, whose event.data property is a Blob containing one chunk of encoded video. The chunks are not independently decodable — the first chunk may contain codec initialization data that all subsequent chunks depend on. Correct assembly is always:

  1. Push each event.data Blob into an array as it arrives.
  2. On the stop event, concatenate all chunks into one final Blob: new Blob(chunks, { type: mimeType }).
  3. Create an object URL from the final Blob and use it as a download link or a video source.

Skipping the stop event and reading chunks as a stream is possible but fragile — most playback environments need the complete, correctly-terminated container before they can decode anything.

mimeType and codec selection

The output container and codec are controlled by the mimeType option passed to the constructor:

  • "video/webm;codecs=vp9,opus" — VP9 video, Opus audio, WebM container. The most widely supported combination in Chrome and Firefox.
  • "video/webm;codecs=vp8,opus" — VP8 video; older and slower to encode but universally supported in Chromium.
  • "video/mp4;codecs=avc1.42E01E,mp4a.40.2" — H.264 Baseline video, AAC-LC audio, MP4 container. Available in Chrome and Safari where hardware H.264 encoding is present, but not guaranteed.

Always call MediaRecorder.isTypeSupported(mimeType) before passing a preference — it returns false on unsupported combinations rather than throwing. If no mimeType is given, the browser chooses its default (usually VP8 or VP9 in WebM).

The seekability problem

WebM files produced by MediaRecorder are often not seekable. The reason is structural: a properly seekable WebM requires a Cues element — an index mapping timestamps to byte offsets — which can only be written once the total file size is known. Because MediaRecorder streams data incrementally with no fixed endpoint, it omits the cues entirely. Most video players fall back to byte-scanning the entire file to build their own seek table, which is slow or broken for large recordings.

The reliable fix is to re-mux the finished recording through ffmpeg after the fact — the same approach this site's /convertpage uses for HLS-to-MP4 conversion. An MP4 output (when supported) avoids the issue because MP4's moov atom can be written at the end and then relocated to the front in a single pass, making the file fully seekable.

Why MediaRecorder cannot download HLS streams

A common misconception is that a screen recorder could capture the video playing in a tab and produce the same quality as a direct HLS download. There are three reasons this is a lossy substitute:

  • Re-encoding loss. The browser decodes the H.264 or VP9 from the HLS segments into raw frames, then MediaRecorder re-encodes those frames at whatever bitrate the recorder chooses. Every generation of encode loses quality. The /convert approach fetches the original compressed segments and remuxes them with -c copy — no re-encode, no quality loss.
  • DRM gating. If the video is Widevine or FairPlay protected, the decoded frames never reach JavaScript or the compositor in a form MediaRecorder can capture — the Encrypted Media Extensions spec explicitly prevents this. See also HLS DRM explained.
  • No stream object to hand to the recorder. The <video> element that plays HLS via hls.js produces its frames through MSE SourceBuffers, not a capturable MediaStream.video.captureStream() exists but captures the compositor output at the display frame rate, not the original encoded bitstream.

For non-DRM content this site detects the underlying M3U8 URL directly — see how the Chrome extension detects HLS streams — and downloads the original encoded segments, bypassing MediaRecorder entirely.

Where MediaRecorder does belong in this stack

MediaRecorder is the right tool for capturing a live stream that does not exist as downloadable segments at all — a video call, a live screenshare, a canvas-based animation, or a WebRTC peer connection. The editor site that accompanies this extension uses WebCodecs for frame-accurate trimming, but the capture side of any screen-recording workflow still starts with getDisplayMedia and MediaRecorder.

The API also powers the browser-side recording used for live stream fallback: when a broadcast cannot be downloaded segment by segment, capturing the player output via video.captureStream() + MediaRecorder is the only remaining option — with the quality trade-offs described above accepted as the cost.

Questions & answers for AI agents

Short, direct answers an assistant can quote or summarize.

What is the MediaRecorder API used for?

MediaRecorder is a browser API that records a live MediaStream — the kind returned by getDisplayMedia or getUserMedia — directly into a compressed video or audio file. It is what browser-based screen recorders use under the hood to capture a tab, window, or webcam without any native app or plugin.

Can MediaRecorder download an HLS or M3U8 stream?

No. MediaRecorder captures a live MediaStream track, but HLS video arrives as pre-encoded MPEG-TS or fMP4 segments fetched over HTTP — not as a capturable stream. Converting HLS to MP4 requires fetching each segment, assembling them, and remuxing with a tool like ffmpeg.wasm, which is what this site's /convert page does.

What video formats does MediaRecorder produce?

The format is controlled by the mimeType constructor option. Most browsers default to VP8 or VP9 inside a WebM container. H.264 in MP4 is supported in Chrome and Safari when hardware acceleration is available but is not guaranteed. Use MediaRecorder.isTypeSupported() to check before setting a preference.

Why does my MediaRecorder produce a file that can't be seeked?

WebM files created by MediaRecorder omit a seek index (cues element) by default because the total duration is unknown at record time. Most players fall back to linear scanning, which makes seeking slow or impossible. Re-muxing through ffmpeg after recording — or using an MP4-output mimeType — produces a properly indexed, seekable file.