Accessing a camera or microphone from a web application has traditionally meant writing JavaScript, requesting permissions, creating a media stream, and connecting that stream to an HTML element.
For many applications, that is more work than necessary.
Chrome 153 adds support for declarative camera and microphone controls through new HTML attributes on media elements. This allows developers to express some media requirements directly in HTML instead of building the entire flow in JavaScript. Chrome's release information for version 153 lists the new controls behavior for camera and microphone elements among its web platform changes.
This article explains how the new approach works, where it fits, how it differs from the existing getUserMedia() model, and what developers should consider before using it in a production application.
The Traditional Way to Access a Camera
Before looking at the newer HTML-based approach, it is useful to understand the traditional JavaScript workflow.
A typical camera application starts with:
const stream = await navigator.mediaDevices.getUserMedia({
video: true
});
The returned stream can then be connected to a video element:
const video = document.querySelector("#camera");
video.srcObject = stream;
The HTML might look like:
<video id="camera" autoplay playsinline></video>
There are several moving parts here:
User action
↓
Request permission
↓
getUserMedia()
↓
Receive MediaStream
↓
Assign stream
↓
Display camera
This model remains important for applications that need detailed control over media streams.
But not every application needs that level of control.
A Declarative Approach
The newer approach lets the browser handle more of the camera or microphone interaction based on HTML.
Instead of writing JavaScript for every part of the initial media interaction, the page can describe the intended media input directly in markup.
The idea is similar to other HTML features:
HTML
↓
Browser understands requirement
↓
Browser handles interaction
↓
User controls permission
This can make simple media workflows easier to build and maintain.
Why Declarative Media Controls Matter
Consider a simple application that needs to let a user select a camera for a video workflow.
With a JavaScript-heavy implementation, developers may need to:
Request permission.
Enumerate devices.
Select the desired device.
Create a media stream.
Handle permission failures.
Attach the stream to the UI.
Stop the stream when it is no longer needed.
For a sophisticated video application, this control is necessary.
For a simple browser feature, it can be excessive.
Declarative media capabilities aim to reduce some of that application-level plumbing.
Camera and Microphone Are Different From Normal HTML Inputs
A camera or microphone is a hardware device.
That means browser security remains important.
A web page cannot simply access a user's camera because an HTML element appears on the page.
The browser still controls access through its permission and security model.
This distinction is important:
HTML declaration
≠
Automatic hardware access without permission
The browser remains responsible for deciding whether access can be granted.
The Existing getUserMedia() API Still Matters
The new declarative capabilities do not make getUserMedia() obsolete.
If an application needs to manipulate a MediaStream, JavaScript is still the appropriate tool.
For example:
const stream = await navigator.mediaDevices.getUserMedia({
video: {
width: 1280,
height: 720
},
audio: true
});
You may need JavaScript when you want to:
Process video frames.
Apply constraints dynamically.
Record media.
Send media through WebRTC.
Select devices programmatically.
Analyze microphone input.
Build custom camera controls.
Combine multiple streams.
The declarative model is most useful when the browser can handle the interaction without application code needing to manage every detail.
Understanding Permissions
Camera and microphone access are sensitive browser capabilities.
A production application should never assume that permission will be granted.
Possible outcomes include:
Permission granted
Permission denied
Permission dismissed
No device available
Device already in use
Browser policy restriction
Your application should have a sensible fallback.
For example:
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: true
});
video.srcObject = stream;
} catch (error) {
console.error("Camera access failed:", error);
}
Even when using newer declarative features, the application should still provide a useful experience when hardware access is unavailable.
HTTPS Is Important
Camera and microphone access are security-sensitive.
Web applications should use a secure context when accessing media devices through browser APIs.
For production:
https://example.com
is the expected deployment model.
Do not design a production camera feature around insecure HTTP.
When testing locally, browsers provide special treatment for local development environments, but production deployment should use HTTPS.
A Simple Camera Application
Consider a basic video meeting page.
The user needs:
Camera preview
Microphone input
Start
Stop
A traditional implementation might contain:
<video id="preview" autoplay playsinline></video>
<button id="start">Start Camera</button>
<button id="stop">Stop Camera</button>
JavaScript then controls the stream:
const preview = document.querySelector("#preview");
let stream;
document.querySelector("#start").addEventListener("click", async () => {
stream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true
});
preview.srcObject = stream;
});
document.querySelector("#stop").addEventListener("click", () => {
if (!stream) {
return;
}
for (const track of stream.getTracks()) {
track.stop();
}
preview.srcObject = null;
});
This is still a good approach when the application needs explicit lifecycle control.
The newer declarative features are more interesting for simpler media interactions where that JavaScript lifecycle is unnecessary.
Why Lifecycle Management Still Matters
A camera stream consumes hardware resources.
Stopping the stream when it is no longer required is important.
For a MediaStream, this means stopping its tracks:
for (const track of stream.getTracks()) {
track.stop();
}
Otherwise, an application may leave a camera or microphone active longer than intended.
A good application should always define:
Start
↓
Use
↓
Stop
↓
Release device
This is particularly important for laptops and mobile devices where camera and microphone state is visible to the user.
Camera Indicators Are Part of the User Experience
Modern browsers typically provide visible indicators when camera or microphone hardware is being accessed.
Do not attempt to hide or work around these indicators.
Instead, design the UI so that the application state matches the browser state.
For example:
Camera:
[ ON ]
Microphone:
[ OFF ]
The user should be able to understand when hardware is active.
This is not just a security consideration.
It is a usability requirement.
Handling Missing Hardware
A production application should handle systems without cameras or microphones.
For example:
const devices = await navigator.mediaDevices.enumerateDevices();
const hasCamera = devices.some(
device => device.kind === "videoinput"
);
const hasMicrophone = devices.some(
device => device.kind === "audioinput"
);
if (!hasCamera) {
console.log("No camera detected.");
}
if (!hasMicrophone) {
console.log("No microphone detected.");
}
Do not assume that every desktop or mobile device has both devices.
This becomes particularly important for:
Join the conversation! Your thoughts help the community grow.