Screen and Tab Capture: getDisplayMedia, System Audio, and the Privacy UX That Matters
Capturing screens, windows, and tabs in the browser — what getDisplayMedia can and can't do, which system audio you actually get, and the privacy indicators users see.
getDisplayMedia() is the API behind every in-browser screen recorder, lecture capture tool, and “show me your screen” feature. It’s also the one with the most browser-specific behavior and the clearest privacy contract — which is exactly why it’s worth understanding properly.
What You Can and Cannot Capture
| Target | Chrome | Firefox | Safari | System Audio? |
|---|---|---|---|---|
| Full screen | Yes | Yes | Yes (15.1+) | Chrome/Edge only |
| Window | Yes | Yes | Yes | No (app-level, Chrome only) |
| Tab | Yes | Yes | Yes | Yes (tab audio) |
The key: browser choice determines what the user can pick — Chrome’s picker shows screen/window/tab; Firefox and Safari limit options. Design for the common denominator.
The Constraints That Matter
const stream = await navigator.mediaDevices.getDisplayMedia({
video: {
frameRate: { ideal: 30, max: 60 },
width: { max: 1920 },
height: { max: 1080 },
displaySurface: 'monitor' // hint: prefer full screen over window/tab
},
audio: {
echoCancellation: false, // don't suppress what you're trying to capture
noiseSuppression: false,
autoGainControl: false
}
});
The System Audio Trap
audio: true in getDisplayMedia captures tab audio only (Chrome/Edge) — not system-wide audio. If you’re recording a video playing in another app, or system sounds, you get silence. The correct pattern: capture the screen with audio: true, then if the user needs app audio, they enable the “share audio” toggle in Chrome’s picker — the API can’t force it.
Privacy Indicators Are Mandatory
The browser forces a persistent indicator — screen recording shows a banner or icon the user cannot hide. This is a feature: it means the user always knows they’re being recorded. The UX implication is the opposite of camera capture: users trust screen recording more because the indicator is unavoidable.
The getDisplayMedia Flow
- User gesture required — same as getUserMedia; call from a click, not on load.
- Picker is browser-controlled — you can’t pre-select the screen; the user chooses.
- Track stops when the user ends sharing — listen for
track.onendedto clean up UI; the OS “Stop sharing” button kills your stream without astop()call from your code.
“The picker is your UX bottleneck. Chrome’s tab picker is three clicks; the first-time user almost always picks the wrong thing. Put a one-line hint in your UI — ‘share the browser tab, not the screen’ — before they open it.”
Capture-recording recipes, audio capture workarounds, and the indicator UX we ship are in the screen capture production guide.