v149 · HTML · demo
Element vs. JS API
The same camera/microphone capability, two different approaches: the legacy getUserMedia() JavaScript call requires custom UI, error handling, and manual state management — the new <usermedia> element does all of that in markup.
Legacy — getUserMedia() JS
Click to request camera access via JavaScript.
<usermedia> — Chrome 149
Click the element above to request camera access.
Denied and recovery branches are ready to simulate.
userchangedenabled: waiting for native toggle or simulation.
Step-by-step comparison
1
Requesting the stream
// JS approach: imperative, requires user activation
navigator.mediaDevices.getUserMedia({ video: true })
.then(stream => { /* handle stream */ })
.catch(err => { /* handle denial */ });
vs.
<!-- HTML approach: declarative, browser controls the button -->
<usermedia onstreamready="handleStream()">
<script type="permissionconstraints">
{ "video": true }
</script>
</usermedia>
With
getUserMedia() you must provide your own button and ensure it's inside a user gesture handler. With <usermedia> the browser renders the button with a trusted label and icon — no custom UI needed.
2
Accessing the stream
// JS: stream arrives in Promise resolve
getUserMedia({ video: true })
.then(stream => {
videoEl.srcObject = stream;
});
vs.
// HTML: stream is a property on the element
function handleStream() {
const um = document.querySelector("usermedia");
videoEl.srcObject = um.stream;
}
After the
streamready event fires, the acquired MediaStream is available as element.stream. No closure or Promise chain required.
3
Muting / unmuting
// JS: must iterate each track
stream.getTracks().forEach(track => {
track.enabled = false; // mute
});
vs.
// HTML: single property, element handles tracks
um.enabled = false; // mute
um.enabled = true; // unmute
// Also fires when user clicks the element's toggle:
um.addEventListener("userchangedenabled", e => {
console.log("user toggled to:", e.target.enabled);
});
The
<usermedia> element's enabled property abstracts track muting. The element also fires userchangedenabled when the user clicks its built-in mute toggle, so you can sync external UI without polling.see also
- Stream Controller — practical mute/unmute demo
- Back to feature index
- ChromeStatus entry