navigator.mediaDevices.enumerateDevices() exposes device counts, IDs, and labels before you ever grant camera or mic access. Here's how trackers use it.
Every laptop with a built-in webcam and microphone, every desktop with a USB headset plugged in, every phone with two cameras — all of it is visible to a web page through one quiet API call. navigator.mediaDevices.enumerateDevices() was designed to let a site list available cameras and microphones before starting a video call. It does that job well, but it also hands over a fingerprinting signal that requires no permission prompt at all, and a richer one the moment a user does grant camera or microphone access.
Key Takeaways
enumerateDevices()runs without any permission prompt and returns the count and kind of connected audio/video devices to any page that calls it.- Before permission is granted,
deviceIdandlabelare blank, but the count and kind of devices (how many cameras, mics, speakers) still add entropy. - After permission is granted (even just once, for any site),
deviceId,groupId, and human-readablelabelstrings — often including the hardware model name — become visible. groupIdlinks devices that share physical hardware, letting a script infer, for example, that a camera and microphone come from the same webcam unit.- Device counts alone are a meaningful cross-site signal: most desktops report a small, stable set of devices that persists across browsing sessions and even across browser restarts.
What enumerateDevices() Returns
The MediaDevices.enumerateDevices() method, part of the Media Capture and Streams specification, returns a promise that resolves to an array of MediaDeviceInfo objects — one per audio input, audio output, and video input device the operating system exposes to the browser. Each object carries four fields:
kind—"audioinput","audiooutput", or"videoinput"deviceId— an opaque, origin-scoped identifier for the devicegroupId— an opaque identifier shared by devices that belong to the same physical hardwarelabel— a human-readable device name, such as "FaceTime HD Camera" or "Logitech BRIO"
Critically, the call itself needs no camera or microphone permission — only getUserMedia() does. A page can call enumerateDevices() on load, before the user has interacted with anything, and immediately learn how many audio and video devices exist on the machine.
Pre-Permission vs. Post-Permission Differences
The spec deliberately blanks deviceId, groupId, and label until the calling origin has been granted camera or microphone access at least once — this is the browser vendors' concession to the fingerprinting risk. But the blanking is partial, not complete, and the gap between the two states is itself informative.
async function getMediaDeviceFingerprint() {
const devices = await navigator.mediaDevices.enumerateDevices();
const counts = { audioinput: 0, audiooutput: 0, videoinput: 0 };
const labeled = [];
for (const d of devices) {
counts[d.kind]++;
if (d.label) {
// Only populated after getUserMedia() has been granted at least once
labeled.push({ kind: d.kind, groupId: d.groupId, label: d.label });
}
}
return { counts, total: devices.length, labeled };
}
Before permission: every deviceId and label is an empty string, but devices.length and the per-kind counts are exact. A machine with one built-in mic, one built-in camera, and a paired Bluetooth headset reports differently from a bare laptop with nothing attached — and that count is stable across every site the user visits, functioning as a low-cardinality but persistent cross-site signal.
After permission: labels and IDs populate immediately, and they tend to leak more than a generic name. Manufacturer and model strings ("Logitech BRIO", "iPhone Microphone") are common, and deviceId values remain stable for that origin across sessions — effectively a semi-persistent identifier scoped to the site, similar in spirit to how persistent visitor IDs survive cache clears through other stable signals.
groupId: Correlating Physical Hardware
groupId exists so applications can avoid picking a microphone and a speaker that would create audio feedback because they're the same physical unit — but it doubles as a correlation key. Two MediaDeviceInfo entries that share a groupId are guaranteed to come from the same piece of hardware, which lets a script infer things like "this camera and this microphone are the same USB webcam" without ever reading a model string. Combined with device counts and any available labels, groupId tightens the fingerprint by ruling out device combinations that couldn't physically occur together.
Device Counts as a Cross-Site Signal
Device enumeration is unusual among fingerprinting techniques in that it needs no rendering pipeline, no GPU, and no timing measurement — it is a direct, structured readout of physical hardware inventory. Combined with signals like WebGL and canvas output, media device counts add another independent axis: two visitors with identical GPUs and identical canvas hashes can still be told apart if one has an external monitor speaker and a webcam mic array that the other lacks. This is the same combinatorial principle covered in our browser fingerprinting guide — no single signal is decisive, but independent signals multiply.
It also interacts with WebRTC in a specific way worth flagging: sites that already request camera/mic access for a legitimate feature (video chat, voice notes) get the fully labeled device list "for free," turning a functional permission grant into a durable, unusually detailed fingerprinting contribution.
Mitigations
- Deny camera/microphone permission by default and grant it per-site only when you intend to use the feature — this keeps
deviceIdandlabelblank on sites that don't need them. - Firefox's
media.navigator.enabledand related prefs, and privacy browsers built on uniformity (Tor Browser), limit or block enumeration entirely for most origins, at the cost of breaking legitimate WebRTC calling features. - Browser permission managers — reviewing and revoking stale camera/microphone grants (
chrome://settings/content/camera, and the Firefox/Safari equivalents) shrinks the set of origins that can see labeled devices at all. - Unplugging unused peripherals reduces device count entropy, though this is impractical for most people and only closes one axis of many.
None of these fully close the pre-permission device-count signal, since blocking enumerateDevices() outright breaks any site that needs to offer a device picker — the realistic goal is limiting how many origins reach the richer, post-permission state.
To see which fingerprinting signals your own browser already exposes — canvas, WebGL, fonts, and more — run BrowserInsight's fingerprint check and gauge how identifiable those signals make you.
Frequently Asked Questions
Does enumerateDevices() require camera or microphone permission?
No. The call itself works without any prompt and returns device count and kind immediately. Only the deviceId, groupId, and label fields require prior getUserMedia() permission from that origin to populate.
Can a site tell how many cameras or microphones I have without asking?
Yes. devices.length and the per-kind breakdown are available before any permission dialog appears, and they persist across visits since they reflect connected hardware rather than something a user resets by clearing cookies.
Does clearing cookies reset my media device fingerprint?
No. The device counts and, once granted, the deviceId/groupId values are derived from hardware and per-origin permission state, not from storage a cookie clear would remove — similar to how canvas and WebGL fingerprints in the browser fingerprinting guide survive a cache clear.
Is media device fingerprinting used on real sites?
Fingerprinting libraries commonly include device counts as one signal among many, and any site with a legitimate video-call or voice-recording feature already has access to the fully labeled list once permission is granted, whether or not it uses that data for tracking.


