The essentials

Quick reference

One focused task per row. Jump to the related section for complete, working examples.

UseSyntaxExamples
Request camera and microphoneconst stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true })View examples
Prefer a video sizevideo: { width: { ideal: 1280 }, height: { ideal: 720 } }View examples
Require a facing modeawait track.applyConstraints({ facingMode: { exact: 'environment' } })View examples
Inspect active settingsconst settings = track.getSettings()View examples
Detect constraint namesconst supported = navigator.mediaDevices.getSupportedConstraints()View examples
List media devicesconst devices = await navigator.mediaDevices.enumerateDevices()View examples
Watch device changesnavigator.mediaDevices.addEventListener('devicechange', refreshDevices)View examples
Prompt for a display surfaceconst display = await navigator.mediaDevices.getDisplayMedia({ video: true, audio: true })View examples
Delegate display capture<iframe src="https://tools.example" allow="display-capture"> </iframe>View examples
Mute outgoing mediatrack.enabled = falseView examples
Release a sourcestream.getTracks().forEach(track => track.stop())View examples
Handle external terminationtrack.addEventListener('ended', handleShareEnded, { once: true })View examples
Record supported mediaconst recorder = new MediaRecorder(stream, { mimeType })View examples
Preview captured mediavideo.srcObject = stream; await video.play()View examples

Media capture exposes sensitive camera, microphone, and display content as MediaStream tracks. Requests require a secure context and a fully active document, and display capture requires a fresh user choice for every request. Ask only in response to an understandable user action, tolerate missing capabilities, show what is live, and stop tracks as soon as the feature ends.

Step by step

Detailed examples

01

Request the least media needed after clear intent

getUserMedia() is available only in secure contexts and requires at least one requested media type. Permission can be denied, ignored indefinitely, restricted by Permissions Policy, or unavailable because another application owns the device. Start from a click, explain why each device is needed, keep a cancelable UI even though the API itself has no signal parameter, and avoid retry loops that repeatedly prompt. Use ideal constraints for preferences and exact/min/max only for hard requirements.

Acquire and preview with recoverable errors
async function startPreview() {
  if (!isSecureContext || !navigator.mediaDevices?.getUserMedia) throw new Error('Capture unavailable');
  const stream = await navigator.mediaDevices.getUserMedia({
    video: { width: { ideal: 1280 }, height: { ideal: 720 } },
    audio: { echoCancellation: true },
  });
  preview.srcObject = stream;
  await preview.play();
  return stream;
}
Back to quick reference ↑
02

Negotiate capabilities instead of assuming hardware

Constraints select and configure a source; they do not guarantee a physical property that the user agent cannot verify. getCapabilities() reports a track's ranges and enumerations, getSettings() reports the chosen result, and applyConstraints() changes a live track. An unsatisfiable required constraint rejects with OverconstrainedError and may reveal capability information before permission, so do not probe devices for fingerprinting. Retain the previous configuration when a change fails and expose a usable fallback.

Switch resolution within advertised capability
async function preferHd(track) {
  const { width } = track.getCapabilities();
  if (!width) return track.getSettings();
  try {
    await track.applyConstraints({ width: { ideal: Math.min(1280, width.max) } });
  } catch (error) {
    if (error.name !== 'OverconstrainedError') throw error;
  }
  return track.getSettings();
}
Back to quick reference ↑
03

Treat device identifiers and labels as permission-scoped data

enumerateDevices() returns only devices available to the document and ordered with defaults first. Labels and nondefault devices may be withheld until capture permission exists. deviceId values are origin-scoped and can change when permission or browsing data changes; never use them as durable user identity. Store a preference only as a hint, recover when it disappears, and unregister devicechange listeners during teardown.

Build choices after permission
async function listCameras() {
  const devices = await navigator.mediaDevices.enumerateDevices();
  return devices.filter(device => device.kind === 'videoinput').map(device => ({
    value: device.deviceId,
    label: device.label || 'Camera',
  }));
}
Back to quick reference ↑
04

Let the user choose every shared display surface

getDisplayMedia() requires transient user activation and the browser must offer the user a choice each time; display permission cannot be persisted as granted. Options such as displaySurface and preferCurrentTab are hints and must not silently restrict the chooser. Video is mandatory, while requested audio may be omitted. The display-capture Permissions Policy defaults to self. Avoid monitor capture when a tab or window is sufficient, warn about recursive capture and confidential content, and clearly identify the live surface.

Start a share and detect browser-side stop
async function shareScreen() {
  const stream = await navigator.mediaDevices.getDisplayMedia({
    video: { displaySurface: 'window' },
    audio: true,
  });
  const [videoTrack] = stream.getVideoTracks();
  videoTrack.addEventListener('ended', () => endShare(stream), { once: true });
  return stream;
}
Back to quick reference ↑
05

Distinguish muting from ending and release devices promptly

Setting enabled to false intentionally emits silence or black frames but keeps the track and often the hardware source alive. The muted property instead reflects that the source temporarily cannot provide data. stop() permanently sets readyState to ended; calling it does not fire the track's ended event. Clone tracks share a source, so the device may remain active until all dependent tracks stop. Track ownership explicitly, stop abandoned acquisitions, clear element srcObject values, and update UI when the browser ends a share.

Own capture through one cleanup function
function endShare(stream) {
  for (const track of stream.getTracks()) track.stop();
  if (preview.srcObject === stream) preview.srcObject = null;
  shareButton.disabled = false;
  liveIndicator.hidden = true;
}
window.addEventListener('pagehide', () => currentStream && endShare(currentStream), { once: true });
Back to quick reference ↑
06

Record in bounded chunks and handle the final data event

MediaRecorder support and container/codec combinations vary. Test isTypeSupported(), omit mimeType when no preferred value is supported, and treat the recorder's mimeType as authoritative. dataavailable chunks may arrive after requestData(), timeslice intervals are approximate, and a final dataavailable precedes stop. Do not hold unbounded recordings in memory: stream chunks to durable storage or a server with size limits. Recording does not grant additional capture permissions and must stop when tracks or the session end.

Record supported chunks and finalize safely
const preferred = 'video/webm;codecs=vp8,opus';
const options = MediaRecorder.isTypeSupported(preferred) ? { mimeType: preferred } : {};
const chunks = [];
const recorder = new MediaRecorder(stream, options);
recorder.addEventListener('dataavailable', event => { if (event.data.size) chunks.push(event.data); });
const complete = new Promise(resolve => recorder.addEventListener('stop', () => resolve(new Blob(chunks, { type: recorder.mimeType })), { once: true }));
recorder.start(5000);
Back to quick reference ↑

Sources and further reading

References

Authoritative documentation used to verify and expand this cheat sheet.

  1. World Wide Web ConsortiumMedia Capture and Streamsw3.org
  2. World Wide Web ConsortiumScreen Capturew3.org
  3. World Wide Web ConsortiumMediaStream Recordingw3.org
  4. World Wide Web ConsortiumPermissions Policyw3.org
  5. World Wide Web ConsortiumSecure Contextsw3.org

Help us improve

Found a typo or missing example?

Tell us what would make this cheat sheet clearer, more complete, or more useful.

Share feedback