guide

Webcam Test Guide

A reference for the desktest.net Webcam Test. Covers what the tool checks against your browser's camera APIs, how to read the verdict, and what to do about each failure.

Open the Webcam Test ->

What does the Webcam Test check?

The tool drives the browser's standard MediaDevices API: enumerateDevices(), then getUserMedia({video: true}), then a live <video> element. No plugin, no extension, no upload. Works on Chrome, Edge, Firefox, and Safari over HTTPS or localhost. The three steps run in order; each must pass before the next:

  1. Camera permission and device detected. Calls enumerateDevices() to count videoinput entries, then requests getUserMedia({video: true}) inside a click handler. After grant, re-enumerates to read the device label.
  2. Video frames are being received. Attaches the MediaStream to a <video>, waits for loadedmetadata, then counts presented frames for 3 seconds. Records actual videoWidth x videoHeight and FPS. Flags low resolution (below 640x480) or low frame rate (below 12 fps).
  3. User confirms the preview. You click Yes if you see yourself, No if the box is black or wrong. Optional snapshot captures one frame into a local canvas; only dimensions go in the report.

When should I run this?

Reading the results

Each step renders a status badge: Pending (grey), Running (yellow), Pass (green), or Fail (red). On fail, the row expands with what the API returned and a numbered checklist.

The verdict panel at the top gives a likely cause and a recommended next step. It reflects the common reason for each failure, not every theoretical one. If wrong for your case, the expanded fix block covers the rest.

Copy Diagnostic Report copies a plain-text report: user-agent, platform, device label, resolution, FPS, frame-counting method, per-step diagnoses, and timestamps. Paste into a ticket.

Take snapshot grabs one frame into a same-tab canvas alongside the live preview. The report records only width and height; the canvas is never uploaded.

Common failures and fixes

The tool produces a named diagnosis for every failure path. Each maps to a fix. In step order:

getusermedia-unsupported: API not available

navigator.mediaDevices.getUserMedia is missing. The API requires a secure context: HTTPS or localhost. Confirm the URL starts with https://. If embedded in a kiosk wrapper, Electron shell, or portal frame, the host may have stripped the API; open in a standalone browser tab. Update browsers older than 2018.

permission-denied: browser blocked the request

getUserMedia threw NotAllowedError, SecurityError, or PermissionDeniedError. Either the user clicked Block, a previous denial is cached, or the OS is blocking the browser.

Then the OS layer. Windows: Settings > Privacy > Camera. Confirm "Camera access" is on and the browser is enabled. macOS: System Settings, Privacy & Security, Camera, browser ticked.

no-input-device: zero cameras enumerated

Either enumerateDevices() returned zero videoinput entries, or getUserMedia threw NotFoundError / OverconstrainedError. The OS does not see a webcam.

  1. Plug the webcam directly into the workstation. Avoid passive USB hubs.
  2. Slide open the laptop privacy shutter (Lenovo ThinkVantage shutter, HP kill switch, Dell ThinkShield).
  3. Check for a sticker or tape over the lens (common in finance and government).
  4. On Windows, open the Camera app. If it also fails, the issue is OS or driver. Check Device Manager under Cameras for a yellow exclamation.
  5. On Linux: v4l2-ctl --list-devices.

stream-stalled: permission granted, no frames

After 3 seconds videoWidth is still 0, or loadedmetadata never fired. Usually one of:

  1. Another app holds the camera. Most webcams cannot be shared. Close Teams, Zoom, OBS, Discord, and the OS Camera app.
  2. USB bandwidth. A hub shared with a scanner or another camera can starve the webcam.
  3. Driver hung. Unplug and replug. If internal, restart the browser then the OS.

low-resolution warning (still passes)

Stream resolved below 640x480. Common on older laptop cameras. Most apps tolerate it; some ID flows reject it. Fix is a better camera.

frame-rate-low warning (still passes)

Measured FPS is below 12. Step 2 passes but calls will look choppy. CPU pegged by background sync or antivirus; low-light auto-exposure integrating each frame longer; or a 1080p camera on a shared USB 2.0 hub. Close apps, add light, or use a direct USB 3.x port.

no-image: user reported nothing visible

Frames are decoding but the user clicked No. The camera is returning black, the wrong angle, or a still frame.

  1. Open the privacy shutter. Lenovo ThinkPad ThinkShutter sits almost flush with the bezel; users miss it constantly.
  2. Remove any sticker, tape, or webcam cover.
  3. Confirm the camera points at the user, not the desk.
  4. In low light, wait 5 to 10 seconds for auto-exposure to settle.
  5. Compare with the OS Camera app. If it also shows black, the issue is hardware, driver, or firmware.

aborted: request cancelled

getUserMedia threw AbortError, or the user closed the confirmation panel. Usually transient. Rerun.

For IT admins

Camera access is governed at two layers: per-site browser permission and OS privacy switch. Both must allow the browser.

Chrome / Edge. Push via GPO, Intune, or Jamf. Keys: VideoCaptureAllowed (global boolean) and VideoCaptureAllowedUrls (URL patterns that bypass the prompt, e.g. https://desktest.net).

Firefox. Push policies.json with Permissions.Camera: an Allow array of origins, plus BlockNewRequests: true.

Windows. The OS gates camera via Settings > Privacy > Camera. Push via Camera Policy CSP ./Vendor/MSFT/Policy/Config/Camera/AllowCamera set to 1. Per-app via AppPrivacy CSP LetAppsAccessCamera. Windows feature updates sometimes reset this; check first if camera dies post-update.

Remote sessions. Citrix CVAD, VMware Horizon, and RDS need the right HDX / RTAV version for camera redirection. The redirected device often negotiates lower resolution than local.

Behind the scenes

The tool calls getUserMedia({video: true}) from a click handler so the browser treats it as a user gesture. Pre-permission enumerateDevices() returns empty label strings (privacy measure); the call is repeated post-grant to read labels.

Frame counting prefers HTMLVideoElement.requestVideoFrameCallback, which fires once per presented frame. That is the only way to measure true camera FPS rather than display refresh. Without it the tool falls back to requestAnimationFrame; the report names which was used.

The <video> element carries autoplay muted playsinline. muted is required because Chrome and Safari refuse to autoplay with audio without a gesture (the stream is video-only anyway). playsinline stops iOS Safari forcing fullscreen. autoplay starts decoding when srcObject is assigned.

Snapshot calls canvas.getContext('2d').drawImage(video, 0, 0) in-tab. The canvas is never converted to a Blob and never uploaded. Only width and height reach the report. On dismissal the tool stops every track so the camera-in-use indicator turns off.