On this pageOPENCLOSE
Media capture that behaves
Use getUserMedia and getDisplayMedia correctly, negotiate constraints, switch devices, and clean up tracks.
Goal
- 01Request camera, microphone, and screen capture without turning permission mistakes into mystery bugs.
- 02Choose constraints that describe intent, then replace tracks when the actual device changes.
- 03Know the difference between muting a track, disabling it, ending it, and stopping it.
The concept, plainly
`getUserMedia()` returns a `MediaStream` containing one or more s. A track is the unit that carries media: one video track, one audio track. The stream is only a container. Adding a track to a peer connection attaches the track itself; stopping the stream does not automatically stop every track unless you stop them yourself.
Permissions are a browser decision, not a JavaScript one. A page cannot silently grant camera access, bypass a denied prompt, or read a device before the user has granted access. Enumeration before permission often returns blank labels; that is expected. Handle `NotAllowedError`, `NotFoundError`, `NotReadableError`, and `OverconstrainedError` as different user-facing problems.
Constraints are a negotiation. `width: { ideal: 1280 }` asks for a preference; `width: { exact: 1280 }` can make the request fail if the device cannot comply. Constraints can be applied at capture time or with `applyConstraints()` on a live track. Do not encode a phone's portrait camera as a 16:9 exact request and then blame WebRTC when it fails.
There are four different ways to make media stop being useful. `track.enabled = false` keeps the track alive and sends black frames or silence. `track.muted` is controlled by the browser when the source disappears. `track.stop()` releases the device and turns off the camera light. Removing a sender from the peer connection stops sending it but the local device may still be open. Decide which behavior the product needs.
Device switching should not rebuild the call. Find the existing sender for that kind, call `replaceTrack(newTrack)`, then stop the old track. Replacing a track usually keeps the transceiver and avoids a renegotiation. Adding a brand-new track or changing direction needs a new offer. Screen sharing uses `getDisplayMedia()` and produces a video track that often ends on its own when the user clicks the browser's Stop sharing control.
Capture is also a privacy boundary. Show a visible local preview, keep a persistent indicator when capture is active, and stop every track on leave or unmount. Content hints such as `detail` can help the encoder preserve sharp screen content, but they are hints, not guarantees. The safest architecture assumes the browser can revoke or end a track at any moment.
See it move
Interactive diagram
From permission prompt to an encoded track
Ask for what you need
Call getUserMedia in response to a user gesture. Request the smallest set of devices the feature actually needs; audio-only calls should not ask for a camera.
The browser owns the device. Your application owns constraints, track policy, and cleanup.
Behind the scenes
What the browser does before JavaScript runs
The permission prompt is outside the page. The browser and operating system decide whether the origin may access a device, and some platforms remember a previous denial. On macOS, the browser itself may lack camera permission in System Settings even when the site prompt was accepted. That is why a robust app reports the DOMException name and offers a clear next step instead of saying 'camera failed'.
Why resolution changes
A camera may expose many modes, but not every combination of width, height, and frame rate. The browser picks the closest supported mode after applying ideal and exact constraints. Then congestion control can lower the encoder resolution and bitrate during the call without changing the track's reported settings. Capture settings, encoder output, and received frame size are three different numbers.
Track replacement is not magic
replaceTrack is cheap when the codec and kind stay the same: the sender swaps its source and continues. It cannot change a video sender into an audio sender, and it does not fix a missing transceiver. If negotiation has not created a video m-line, there is nowhere to put the video track. Check the transceiver direction first, then replace.
Code walkthrough
Request only what the feature needs and map browser failures to actions a user can take.
export async function captureAudioVideo() {if (!navigator.mediaDevices?.getUserMedia) {throw new Error("This browser has no getUserMedia. Use HTTPS or localhost.");}try {const stream = await navigator.mediaDevices.getUserMedia({audio: {echoCancellation: true,noiseSuppression: true,autoGainControl: true,},video: {facingMode: "user",width: { ideal: 1280 },height: { ideal: 720 },frameRate: { ideal: 30, max: 60 },},});for (const track of stream.getTracks()) {track.addEventListener("ended", () => {console.info("track ended", track.kind, track.label);});}return stream;} catch (error) {const name = error instanceof DOMException ? error.name : "Error";if (name === "NotAllowedError") throw new Error("Camera permission was denied. Allow it in site settings, or use the test feed.");if (name === "NotFoundError") throw new Error("No camera or microphone was found. Connect a device or use the test feed.");if (name === "NotReadableError") throw new Error("The device is busy in another application. Close that app and retry.");if (name === "OverconstrainedError") throw new Error("The requested constraints cannot be met by this device. Relax exact values.");throw error;}}
Do and don't
Do
Request capture after a user action
A click makes the permission prompt understandable and avoids autoplay and policy surprises.
Inspect the resolved settings
Read track.getSettings() and display the real resolution and frame rate. Do not repeat the constraints you asked for as if they were facts.
Stop tracks on teardown
Stop local tracks, remove srcObject references, close the peer connection, and release any canvas animation frames.
Offer a fallback source
A synthetic canvas feed keeps the lesson and the lab usable when hardware, permission, or HTTPS is unavailable.
Don't
Don't set every constraint to exact
Exact values turn a preference into a hard failure. Reserve them for cases where a mismatch genuinely makes the feature impossible.
Don't confuse disable with stop
Disabling sends black frames or silence while the device stays open. Stopping releases the device and cannot be undone.
Don't rebuild the peer connection for a device change
replaceTrack preserves the session. Tearing down the call for a camera switch causes avoidable freezes and signaling churn.
Don't forget screen-share end events
Users stop sharing from browser chrome, not your button. Without onended, the app can keep showing a dead frame.
Practice
Hands-on lab
Capture, switch, and release
Use the media stage to request camera and microphone, switch devices, mute tracks, and release everything. Then watch a test pattern travel over a real peer connection.
- 1Start camera and microphone, then inspect the displayed track settings and input level.
- 2Mute the microphone. The audio track remains live; only the payload changes.
- 3Switch to the synthetic test feed and confirm that it travels through the same sender and ICE path.
- 4Stop media, close the peer connection, and verify the camera light turns off.
Solo? If no camera is available, the generated test feed exercises the exact same MediaStreamTrack and sender path.
Quick quiz
Knowledge check
0 of 4 answered
- 01
What does setting track.enabled = false do?
- 02
Which call replaces a camera source without a new offer in the common case?
- 03
Why are device labels sometimes blank?
- 04
A screen-share track ends because the user clicked the browser's stop control. What should the app listen for?
Key takeaways
- A MediaStream is a container; the track is the resource with lifecycle and state.01
- Ideal constraints express intent, exact constraints make hard demands, and the resolved settings are the truth.02
- replaceTrack is the right tool for device changes; new tracks or directions may need renegotiation.03
- Stop tracks explicitly, and always provide a synthetic or audio-only fallback when hardware is unavailable.04
Common errors and fixes
Terms in this lesson
Done with this lesson?
Mark it complete, then keep the thread.