← Chrome 153 reference

v153 · html · web api · shipped

Capability elements: <camera> and <microphone>

Limited availability

  • Chrome · 153 (milestone listing)
  • Edge · no separate signal (Chromium-based)
  • Firefox · no signal
  • Safari · no signal

Not on the Baseline register as its own entry: the closest webstatus.dev feature is usermedia (Baseline “limited”), and this feature's ChromeStatus record tags the generic permissions web-feature. Chrome-only in 153; Firefox and WebKit positions are both “No signal”.

Two declarative, user-activated HTML controls from the capability-elements (ex-PEPC) family: <camera> requests video capture only, <microphone> requests audio capture only. Like the <usermedia> MVP that shipped in Chrome 151, each embeds a browser-controlled, strictly styled button that runs the permission flow and the capture request for you, then hands the resulting media to the page — but scoped to a single capability instead of a combined camera+microphone stream.

Mixed stability — read before copying The ChromeStatus milestone=153 listing files this feature as “Enabled by default” (verified 2026-07-28), and the Intent to Ship states “Will ship enabled for all users”. However, as of 2026-07-28 the trunk runtime feature CameraAndMicrophoneElements in runtime_enabled_features.json5 carries no status field — and per that file's own header, features without a status are not enabled anywhere by default. The stable-status flip is therefore pending at trunk (or branch-level) at fetch time: feature-detect ("HTMLCameraElement" in window) before relying on the elements in any given build. The specification is a working draft with explicitly unfinished sections (noted below).

at a glance

Shipped inChrome 153 (desktop, Android) — milestone listing: “Enabled by default” (the listing is authoritative). The Intent to Ship estimated desktop + Android 152; the live listing and ship stage record 153
Runtime featureCameraAndMicrophoneElements (single flag gates both elements; constructors CHECK it) — see the stability note above for its current trunk status
InterfacesHTMLCameraElement, HTMLMicrophoneElement — both include the shared HTMLMediaCaptureElementBase mixin
SpecMedia Capture and Streams Extensions — Media capture HTML elements (working draft; the ChromeStatus spec link's #the-camera-html-element fragment does not resolve in the current draft)
ExplainerMedia Capture Elements explainer (w3c/mediacapture-extensions)
TAG revieww3ctag/design-reviews #1218 (shared with the <usermedia> MVP; status “issues addressed” per the intent)
ChromeStatus5153829504024576 — Capability elements: <camera> and <microphone>

why they exist

The <usermedia> MVP (gendn reference, Chrome 151) proved the model: make the element itself the trusted control — a click on a browser-rendered button is an unambiguous user gesture, the browser runs the permission UX inline (with an in-page recovery path for previously-denied users), performs the capture request, and delivers the media. But <usermedia> always brokers a combined camera+microphone request. Many applications need exactly one: a voice recorder wants audio only, a document scanner wants video only. Requesting both wastes a permission decision and confuses users.

<camera> and <microphone> are the single-capability siblings: the same mechanism, security model, strict styling, and permission-recovery path, each scoped to one kind of capture — <camera> installs a VIDEO_CAPTURE permission descriptor, <microphone> an AUDIO_CAPTURE one. The feature continues the PEPC (Page Embedded Permission Control) lineage: generic <permission> element → <usermedia> MVP → single-capability elements.

Source: chromestatus.com/feature/5153829504024576; blink-dev Intent to Ship (2026-07-22); Chromium html_camera_element.cc; Chromium html_microphone_element.cc.

Syntax

<!-- Declarative: no required attributes. Children are fallback content. -->
<camera>Camera access required</camera>
<microphone>Microphone access required</microphone>

<script>
  const cam = document.querySelector("camera");
  // Optional: configure the capture request the element will make.
  cam.setConstraints({ video: { width: 1280 } });
</script>

The normative IDL from the specification (HTMLMicrophoneElement is identical except for the name):

[Exposed=Window]
interface HTMLCameraElement : HTMLElement {
  [HTMLConstructor] constructor();

  readonly attribute MediaStreamTrack? track;
  readonly attribute DOMException? error;
  undefined setConstraints(optional MediaTrackConstraintSet constraints = {});

  attribute EventHandler ontrack;
  attribute EventHandler onerror;
  attribute EventHandler oncancel;
};

Chromium's shipping IDL diverges from the draft (see spec vs shipping). The shipping surface, composed from html_camera_element.idl / html_microphone_element.idl plus the shared HTMLMediaCaptureElementBase mixin and its two modules/mediastream partials (stream, setConstraints):

[Exposed=Window, RuntimeEnabled=CameraAndMicrophoneElements, HTMLConstructor]
interface HTMLCameraElement : HTMLElement {};
HTMLCameraElement includes HTMLMediaCaptureElementBase;
// (same for HTMLMicrophoneElement)

interface mixin HTMLMediaCaptureElementBase {
  readonly attribute DOMException? error;
  attribute EventHandler onstream;   // NOT "ontrack" (divergence)
  attribute EventHandler oncancel;
  attribute EventHandler onerror;
};
// partial mixins applying to all three capability elements:
partial interface mixin HTMLMediaCaptureElementBase {
  readonly attribute MediaStream? stream;                    // NOT "track" (divergence)
  undefined setConstraints(optional HTMLMediaStreamConstraints constraints = {});
};

dictionary HTMLMediaStreamConstraints {
  MediaTrackConstraintSet video;
  MediaTrackConstraintSet audio;
};

Content model: both elements belong to the flow, phrasing, interactive, and palpable content categories; they can appear wherever phrasing content is expected, and any children serve as fallback content (rendered when the element can't operate — for example in a non-secure context). Presentation is a user-agent-controlled button whose label and icon reflect the underlying track state (requested, live, muted, stopped). Both constructors CHECK the CameraAndMicrophoneElements runtime feature.

Source: Media Capture and Streams Extensions — Media capture HTML elements; Chromium html_camera_element.idl; Chromium html_microphone_element.idl; Chromium html_media_capture_element_base.idl.

Inputs

Neither element takes required content attributes — <camera></camera> alone requests video with default settings, <microphone></microphone> audio. Three input channels exist:

Source: Specification — content model and activation; Chromium user_media_element_constraints.cc; Chromium user_media_request_provider_impl.cc.

Outputs

Three observable outputs:

The browser-rendered button itself is also an output: its label and icon track the underlying capture state without any page code.

Source: Chromium user_media_request_provider_impl.cc — OnSuccess/OnError; Chromium html_media_capture_element_base.cc; Specification — event model.

Errors

Failures never throw synchronously from markup or clicks; they surface through the error property and the cancel/error events. The exact mapping, from Chromium's implementation (shared with <usermedia>):

Failure paths — what fires and what error holds
CauseEventerror value
User dismisses or denies the permission promptcancelNotAllowedError (“Permission dismissed” / “Permission denied”)
Activation without transient user activationerrorInvalidStateError (“The permission element activation must be triggered by a user gesture.”; a DevTools audits issue is also reported)
Stream creation throws during request setuperrorOperationError (“Stream creation failed”)
Underlying capture request rejects (device busy, no device, …)errorThe rejection DOMException (e.g. NotFoundError, NotReadableError, NotAllowedError, OverconstrainedError)
Constraints missing the element's kind (defensive branch)errorNotSupportedError (“No video constraints set” / “No audio constraints set”) — present in user_media_request_provider_impl.cc but not reachable through the current JS surface, because setConstraints() backfills both members and the no-call path installs defaults
Non-secure contextnoneThe element renders its fallback content; Chromium reports a DevTools audits issue

Recovery is always the same shape: read el.error in the terminal event handler, show your own recovery UI, and let the user click the element again — a failed attempt re-arms the element. The draft specification's error-description section is explicitly marked “Description still TBD”, so treat the table above as the shipping-contract source of truth.

Source: Chromium html_media_capture_element_base.cc — OnEmbeddedPermissionsDecided, DefaultEventHandler, OnActivationFailed; Chromium user_media_request_provider_impl.cc; Specification — activation algorithms (TBD note).

Context

Exposure: both interfaces are [Exposed=Window] — no worker exposure. Like getUserMedia(), the elements only operate in a secure context; elsewhere they show fallback content. Feature-detect with "HTMLCameraElement" in window / "HTMLMicrophoneElement" in window — on unsupported engines the tags parse as unknown elements and your fallback content renders.

Activation requirements: acquisition starts only from a genuine user gesture — the click must carry transient user activation; programmatic or untrusted activation is rejected (a DevTools audits issue, then an error event with InvalidStateError where the activation path reaches the element). The browser, not the site, mediates the camera/microphone permission, and may skip the prompt entirely when the user has already granted it.

Default capture: each element installs exactly one permission descriptor — VIDEO_CAPTURE for <camera>, AUDIO_CAPTURE for <microphone> — and the capture request is built with only the matching kind, even though the constraints dictionary has both members.

Platform scope: the Intent to Ship answers “No” to support on all six Blink platforms without naming the excluded set; the same mechanism's <usermedia> MVP excludes Android WebView (it requires permission-manager support). Treat WebView availability as an explicit unknown.

Source: Chromium html_media_capture_element_base.cc; Chromium html_camera_element.cc — ApplyDefaultConstraints; Chromium user_media_request_provider_impl.cc — kind-scoped request construction; Intent to Ship — platform question.

Lifecycle

Each element is a small state machine:

  1. Request statestream is null. A trusted click starts acquisition: the embedded permission flow runs first (prompt only if needed), then the browser performs the capture request with the configured constraints for the element's kind.
  2. Terminal event — exactly one of stream / cancel / error fires per attempt (queued as a DOM-manipulation task). On success the stream supplement is populated before the stream event fires.
  3. Active state — while the associated MediaStream is active, further clicks do not start a new request (the provider returns early when an active stream exists). The elements do not become mute/unmute toggles in the shipping implementation — the draft spec likewise says activation with an existing track “currently ignores activation … The behaviour for an element with a track still needs to be determined”. Track muting stays with your own UI or MediaStreamTrack.enabled.
  4. Reset — once the stream is no longer active (including external track.stop()), the next click starts a fresh acquisition. A failed attempt also re-arms the element immediately.
Source: Chromium user_media_request_provider_impl.cc — active-stream early return; Specification — activation behavior (TBD note); Explainer — user journey.

Examples

<camera id="cam">This page needs your camera.</camera>
<microphone id="mic">This page needs your microphone.</microphone>
<video id="preview" autoplay playsinline muted></video>

<script>
  const cam = document.getElementById("cam");
  const mic = document.getElementById("mic");
  const video = document.getElementById("preview");

  // Optional configuration; call before the user interacts.
  // First call wins — later calls are ignored (see setConstraints reference).
  cam.setConstraints({ video: { width: 1280, facingMode: "environment" } });
  mic.setConstraints({ audio: { echoCancellation: true, noiseSuppression: true } });

  cam.addEventListener("stream", () => {
    // Success: cam.stream is a live MediaStream with one video track.
    video.srcObject = cam.stream;
  });
  mic.addEventListener("stream", () => {
    // mic.stream is a live MediaStream with one audio track.
  });
  for (const el of [cam, mic]) {
    el.addEventListener("cancel", () => {
      console.log("Prompt dismissed or denied:", el.error?.message);
    });
    el.addEventListener("error", () => {
      console.error("Capture failed:", el.error?.name, el.error?.message);
    });
  }
</script>

No getUserMedia() call, no promise handling, no permission re-prompt logic — the browser owns the entire acquisition flow. The Chrome Platform Showcase has no demo for this feature yet (route checked 404 on 2026-07-28), so there is no embedded live example; the sibling <usermedia> demo exercises the same underlying mechanism.

Source: Explainer — examples; showcase route HEAD check 2026-07-28.

Compatibility

<camera> / <microphone> elements — checked 2026-07-28
Engine / runtimeSupportNotes
Chrome / Edge (desktop)153 (listing)Milestone listing: Enabled by default. Caveat: trunk CameraAndMicrophoneElements carries no status: stable as of 2026-07-28 — flip pending; feature-detect before relying
Chrome (Android)153 (listing)Per the ship stage (desktop 153, Android 153). Android WebView: explicit unknown — the intent answers “No” to all-six-platforms support without naming exclusions
FirefoxNot supportedVendor position “No signal” (ChromeStatus record)
SafariNot supportedVendor position “No signal” (ChromeStatus record)
BCDNo entryNo api/HTMLCameraElement.json or api/HTMLMicrophoneElement.json in browser-compat-data as of 2026-07-28 — compat data above is interim, from the chromestatus listing
WPTNone found upstreamZero html/semantics/permission-element/ camera/microphone tests in the public WPT tree (Chrome 151 stable run summary, fetched 2026-07-28; the sibling usermedia suite has 13 files). This contradicts the intent's “fully tested by web-platform-tests: Yes” answer — flagged for re-check

MDN has no page for either element or interface as of 2026-07-28 (HTMLCameraElement and HTMLMicrophoneElement both 404), which is why this reference exists. Always feature-detect and keep meaningful fallback content inside the tags.

Source: ChromeStatus API feature record (stages, vendor positions); Chromium runtime_enabled_features.json5; WPT storage summary (Chrome 151 stable, 2026-07-26 run).

Security and privacy

Source: Explainer — privacy and security considerations; Specification — activation filtering; TAG design review #1218.

spec vs shipping — known divergences

The working-draft specification and Chromium's shipping IDL currently disagree on the surface shape. The shipping contract (what your code actually sees in Chrome) wins for implementation; the draft may converge later:

SurfaceDraft specShipping Chromium
Media deliverytrack attribute (MediaStreamTrack?)stream attribute (MediaStream?) — the single track is inside the stream (partial mixin IDL)
Success eventtrack event / ontrackstream event / onstream (mixin IDL; dispatched in provider OnSuccess)
Constraints parametersetConstraints(optional MediaTrackConstraintSet constraints = {})setConstraints(optional HTMLMediaStreamConstraints constraints = {}) — the two-member {video, audio} dictionary shared with <usermedia> (constraints IDL)
Click with active media“currently ignores activation … behaviour … still needs to be determined”Also ignored (early return on active stream) — no mute toggle ships
Error descriptions“Description still TBD”Exact DOMException mapping in the errors table
Source: Media Capture and Streams Extensions — Media capture HTML elements; Chromium IDL/implementation files linked per row.

member references

see also