v150 · Web APIs · Permission Flow

Permission Flow

Comparing the JavaScript getUserMedia() approach with the new <usermedia> declarative element — focusing on when and how permission prompts appear and what the user experiences.

The fundamental improvement of <usermedia> is trust: the browser controls the permission prompt timing as part of an element activation it fully recognises, rather than relying on JavaScript to properly manage user gesture context.

flow simulator

  1. Choose a flow to see who owns the prompt timing.
Native support probe has not run yet.

flow comparison

getUserMedia() — JavaScript approach

  • 1. Developer decides when to call getUserMedia()
  • 2. Browser checks if call is within a "user gesture" — rules are complex
  • 3. Browsers may block the call if gesture context is unclear (e.g., after a setTimeout)
  • 4. If allowed: permission prompt appears — but user may not understand why
  • 5. Promise resolves/rejects with stream or error
Problem: permissions can appear at unexpected times from the user's perspective — any JS call path can trigger them.

<usermedia> — declarative approach

  • 1. Developer places <usermedia> in HTML and configures it with setConstraints()
  • 2. Browser renders it as an interactive element the user can activate
  • 3. User clicks/taps — browser handles the activation, no JS needed
  • 4. Browser shows permission prompt as part of the activation flow
  • 5. stream fires on success; error or cancel fires otherwise
Benefit: permission always tied to a visible, meaningful user action. Browser, not JS, controls the timing.

feature comparison

Aspect getUserMedia() <usermedia>
User gesture required Partial — enforced by browser heuristics Always — element activation = explicit gesture
Prompt timing control Developer (can misuse) Browser (always correct)
Browser-owned prompt surface No Yes — native element owns activation UI
Fine-grained constraints Yes — full MediaStreamConstraints Yes — configured with setConstraints()
Browser support All browsers Chrome 150+ (MVP)

progressive enhancement

<!-- Graceful degradation: usermedia for Chrome 150+,
     button+JS as fallback -->
<usermedia id="um">
  <!-- Content shown in browsers without <usermedia> -->
  <button id="fallback-btn">Start camera</button>
</usermedia>

<script>
  const um = document.getElementById('um');
  const constraints = { video: {}, audio: {} };

  // Check if <usermedia> is supported
  const hasUsermedia =
    typeof HTMLUserMediaElement !== 'undefined' &&
    um instanceof HTMLUserMediaElement &&
    typeof um.setConstraints === 'function';

  if (hasUsermedia) {
    um.setConstraints(constraints);
    um.addEventListener('stream', () => startPreview(um.stream));
    um.addEventListener('error', () => showError(um.error));
    um.addEventListener('cancel', () => showCancelled());
  } else {
    // Fallback: use getUserMedia directly
    document.getElementById('fallback-btn').addEventListener('click', async () => {
      try {
        const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
        startPreview(stream);
      } catch (err) {
        showError(err);
      }
    });
  }
</script>

see also

implementation reference

Need the exact API surface, compatibility boundaries, errors, lifecycle, and source links? Read the matching gendn reference ↗