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
- Choose a flow to see who owns the prompt timing.
Native support probe has not run yet.
flow comparison
getUserMedia() — JavaScript approach
-
Developer decides when to call
getUserMedia() - Browser checks if call is within a "user gesture" — rules are complex
- Browsers may block the call if gesture context is unclear (e.g., after a setTimeout)
- If allowed: permission prompt appears — but user may not understand why
- 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
-
Developer places
<usermedia>in HTML and configures it withsetConstraints() - Browser renders it as an interactive element the user can activate
- User clicks/taps — browser handles the activation, no JS needed
- Browser shows permission prompt as part of the activation flow
-
streamfires on success;errororcancelfires 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 ↗