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.

HTMLUserMediaElement in this browser checking…
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