About this screen recorder

A small, dependency-free tool for one job: capturing your screen, a window, or a browser tab, with optional microphone audio, and handing you a downloadable video. No account, no upload, nothing installed.

Why it exists

Every mainstream desktop browser has shipped the two APIs a screen recorder needs — getDisplayMedia and MediaRecorder — for years, yet reaching for one of them usually still means installing a desktop app or a browser extension with its own permissions, update cycle, and often its own watermark or trial limit. This page is the plain version: it calls the same APIs any installed recorder would, and stops there.

How it is implemented

Three moving parts, in the order they run:

  1. Capture. A click on Start recording calls navigator.mediaDevices.getDisplayMedia({ video, audio }). The call must originate from a real user gesture — a browser will reject it if it is called any other way — and the resulting picker, source selection, and any "share tab audio" option are entirely the browser's own UI, not this page's.
  2. Optional microphone mixing. If the microphone toggle is on, getUserMedia({ audio: true }) opens a second stream. When both a microphone track and screen/tab audio exist at once, they are combined with a Web Audio MediaStreamAudioDestinationNode — each source is connected to it as an input, and its own output stream carries the mixed result. When there is only one audio source, it is attached directly with no mixing step, which keeps the common case (mic-only or tab-audio-only) simple and avoids spinning up an AudioContext unnecessarily.
  3. Encoding. The combined stream is handed to MediaRecorder, which is asked (via MediaRecorder.isTypeSupported) to use the best codec combination the browser actually supports from a preference list — VP9+Opus first, then VP8+Opus, then bare WebM, then MP4 as a last resort for engines that do not produce WebM at all. Recorded data arrives in one-second chunks so a crash or an unexpected tab close still leaves a partial, playable file rather than nothing. On Stop — or when the browser's own "Stop sharing" control ends the capture directly — the chunks are assembled into one Blob, played back in a local <video>, and offered as a download link built from that Blob.

Every media track this tool opens — video, tab/display audio, and microphone — is stopped the moment recording ends, the Stop button is pressed, or you navigate away mid-recording (caught via the pagehide event). The object URL backing the download link is revoked at the same time on navigation, so nothing is left running or referenced after you leave the page.

How it was tested

The parts of this tool that do not touch the DOM or a real camera/microphone — picking a supported codec, formatting the elapsed timer, formatting the file size, building the download filename, and writing the friendly permission-error messages — are pulled out into a plain module with a Node-testable exports hatch, and exercised with 40+ assertions covering:

  • The mimeType picker choosing the first supported candidate from the preference list, falling back to an empty string (letting the browser choose its own default) when nothing on the list is supported or when there is no isTypeSupported function at all.
  • Timer formatting at the exact minute and hour boundaries (59s, 60s, 3599s, 3600s), and that negative or NaN input never reaches the display as a broken string.
  • File-size formatting across the byte/KB/MB/GB boundaries.
  • The generated download filename, including that an invalid Date falls back to the current time rather than embedding NaN in the name visitors would see.

What that suite cannot exercise is the real getDisplayMedia picker, real hardware permission prompts, or the browser's own "Stop sharing" control — those need a real browser, a real user gesture, and (for the fullest test) a second monitor or window to actually share. That part was checked by hand against a real Chromium browser before shipping, including the abrupt-stop path via the browser's own sharing indicator.

How the site is built

  • Static HTML, one small stylesheet, one small script. No framework, no build step, no bundler.
  • Nothing loads from a CDN. No remote font, no remote script, no remote image; the favicon and header glyph are inline SVG. Once the page has loaded, the recorder itself needs no further network activity to function.
  • System font stack, so nothing has to download and nothing shifts as the page loads.
  • The recording never leaves your device unless you explicitly click Download and then choose to send that file somewhere yourself.
  • Dark mode follows your system setting via prefers-color-scheme.

Scope and honesty about limits

This is a capture-and-download tool, not an editor. It does not trim, caption, compress beyond what the chosen codec already does, or merge multiple recordings into one. It cannot force a browser to offer tab/system audio where that browser's own picker does not provide the option, and it cannot run at all on platforms — iOS Safari chief among them — that simply do not expose getDisplayMedia to web pages. See the browser support table on the main tool page for the specifics.

Recorded chunks are held in the browser's memory until the file is assembled, so an extremely long, high-resolution capture is bounded by your device's available RAM rather than by any limit this tool sets.