← Capability elements: <camera> and <microphone>

v153 · html · property reference

stream / error

The two read-only properties you use after a <camera> or <microphone> element's terminal event: stream holds the captured MediaStream on success; error holds the failure DOMException otherwise. Both are null until the corresponding outcome.

Syntax

// Shipping Chromium IDL (both interfaces, via the shared mixin + partial):
readonly attribute MediaStream? stream;    // partial mixin HTMLMediaCaptureElementBase
readonly attribute DOMException? error;    // interface mixin HTMLMediaCaptureElementBase

// Draft spec IDL (diverges — see overview "spec vs shipping"):
//   readonly attribute MediaStreamTrack? track;
//   readonly attribute DOMException? error;

In Chromium both properties live on the shared HTMLMediaCaptureElementBase mixin that HTMLCameraElement and HTMLMicrophoneElement include: error in the base mixin IDL, stream in the modules/mediastream partial (supplement-implemented, shared with <usermedia>). The draft specification instead defines a track attribute of type MediaStreamTrack? — there is no track property in the shipping implementation.

Source: Chromium html_media_capture_element_base.idl; Chromium html_user_media_element_media_stream.idl; Specification — element IDL.

Inputs

Both are parameterless getters — no inputs. What you read depends on the element's state: before any activation attempt both return null; after a successful acquisition stream is non-null; after a failed or cancelled attempt error is non-null.

Source: Chromium html_user_media_element_media_stream.cc — stream getter; Chromium html_media_capture_element_base.h — error getter.

Outputs

stream returns a MediaStream whose track set contains the single captured track — kind video for <camera>, kind audio for <microphone> — or null. The provider assigns it via the supplement before dispatching the stream event, so reading it inside the stream handler is always safe. Attach it straight to a media element (video.srcObject = el.stream) or read its tracks (el.stream.getVideoTracks() / getAudioTracks()).

error returns a DOMException describing the terminal failure — see the exact name/message mapping in the overview errors table — or null. It is set before the cancel/error event is queued.

Source: Chromium user_media_request_provider_impl.cc — SetMediaStream then kStream; SetError then kCancel/kError.

Errors

The getters themselves never throw. All failure information flows through error as a value: NotAllowedError (dismissed/denied), InvalidStateError (activation without a user gesture), OperationError (“Stream creation failed”), NotSupportedError (defensive missing-constraints branch — see the overview table for why it is not reachable via the JS surface), or the capture request's own rejection DOMException.

Source: Chromium html_media_capture_element_base.cc; Chromium user_media_request_provider_impl.cc.

Context

Readable on any <camera>/<microphone> element instance in a Window context (the interfaces are [Exposed=Window]). Both getters exist whenever the CameraAndMicrophoneElements runtime feature created the element — reading them on a fallback-rendered unknown element in an unsupported engine simply yields undefined (the interface doesn't exist there), which doubles as a feature probe alongside "HTMLCameraElement" in window.

Source: Chromium html_camera_element.idl; Chromium html_microphone_element.idl.

Lifecycle

  1. Initial: stream === null, error === null (slots initialized empty at construction).
  2. Success: provider calls SetMediaStream(stream) then queues the stream event — stream is non-null from that point until the stream ends (the supplement holds the reference; track stop/inactivity transitions the element back to the request state, and the next acquisition replaces it).
  3. Failure: SetError(domException) runs before the cancel/error event is queued — error is readable inside the handler.
  4. Re-arm: a new attempt resets the request bookkeeping (ResetMediaStreamRequestTime); a later success overwrites the supplement's stream reference.
Source: Chromium user_media_request_provider_impl.cc — OnSuccess/OnError ordering; Chromium html_user_media_element_media_stream.cc.

Examples

const cam = document.querySelector("camera");

cam.addEventListener("stream", () => {
  const stream = cam.stream;            // MediaStream, never null here
  const [track] = stream.getVideoTracks();
  console.log("captured:", track.kind, track.label, track.readyState);
  document.querySelector("video").srcObject = stream;
});

cam.addEventListener("error", () => {
  const err = cam.error;                // DOMException, never null here
  if (err.name === "NotAllowedError") showPermissionHelp();
  else showRetry(err.message);
});

// Feature probe fallback:
if (!("HTMLCameraElement" in window)) {
  // element parsed as unknown; its fallback children are already visible
}
Source: Chromium user_media_request_provider_impl.cc.

Compatibility

stream / error getters — checked 2026-07-28
Engine / runtimeSupportNotes
Chrome / Edge (desktop)153 (listing)Same availability as the elements themselves; trunk flag status caveat in the overview table
Chrome (Android)153 (listing)WebView: explicit unknown (see overview)
Firefox / SafariNot supportedNo signal on the ChromeStatus record
BCD / MDNNo entriesNo BCD files; both MDN interface pages 404 (2026-07-28)
Source: ChromeStatus API feature record.

Security and privacy

stream hands the page a live capture stream: treat the click-to-grant as instant hardware activation (LED/indicators on). The stream is only ever delivered after the browser's own permission mediation — the property cannot be set from script (read-only, supplement-assigned). error exposes standard getUserMedia()-class failure reasons (device presence, busy state) — the same disclosure class as the imperative API, no new fingerprint surface.

Source: Explainer — security and privacy considerations; stream IDL (readonly).