← 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.
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.
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.
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.
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.
Lifecycle
- Initial:
stream === null,error === null(slots initialized empty at construction). - Success: provider calls
SetMediaStream(stream)then queues thestreamevent —streamis 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). - Failure:
SetError(domException)runs before thecancel/errorevent is queued —erroris readable inside the handler. - Re-arm: a new attempt resets the request bookkeeping (
ResetMediaStreamRequestTime); a later success overwrites the supplement's stream reference.
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
| Engine / runtime | Support | Notes |
|---|---|---|
| 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 / Safari | Not supported | No signal on the ChromeStatus record |
| BCD / MDN | No entries | No BCD files; both MDN interface pages 404 (2026-07-28) |
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.