enumerateDevices exposes audiooutput on a Chromium claim
Check id media-kinds-vs-engine
What an ordinary browser yields
On a Chromium-claiming UA, the device kinds include audiooutput (severity: warn).
What a detector infers
The probe calls navigator.mediaDevices.enumerateDevices() and keeps only each entry's .kind, sorted — never labels or deviceIds, which is what makes the reading safe to display and permission-independent. It then compares that kind set against the engine family the User-Agent claims, derived by claimedEngine(): a UA matching edg/chrome/chromium/crios maps to V8. The check runs only when the UA claims V8 and the kind list is non-empty, and passes when the list contains "audiooutput". The mechanism is a real engine-level divergence: Chromium enumerates audiooutput devices, Gecko does not implement that kind. So a device list shaped like Firefox's while the UA claims Chromium is a contradiction between two independent surfaces, and a detector can infer that the UA string was changed while the underlying media stack was not.
How to resolve it
Present a device list consistent with the claimed engine, or leave enumerateDevices native. If you spoof a Chromium UA, the enumerated kinds must include audiooutput as Chromium's own stack does. The row is skipped entirely — not failed — when the UA claims a non-V8 family, when enumerateDevices is unavailable, or when the list is empty, so a device-less container is never flagged on this basis.
Nearby checks in Environment & locale
- the analyser byte spectrum is the defined quantisation of its float spectrum
audio-analyser-float-vs-byteAn AnalyserNode publishes the same spectrum twice. getFloatFrequencyData returns decibels, and getByteFrequencyData returns that spectrum… - speech synthesis voices (host OS speech engine)
speech-voicesspeechSynthesis.getVoices() enumerates the host operating system's speech engine — a resource stealth stacks essentially never touch, which… - the voice list arrives the way Chrome delivers it
speech-voices-async-pathspeech-voices reads WHAT is in the voice list. This reads HOW the list arrived, which is a separate fact and survives a roster whose every… - an OS that ships a speech synthesiser enumerates at least one voice
speech-voices-vs-platformThe plain speech-voices readout above refuses to score an empty list, and as written it is right to: Chrome does not build the voice list in… - a live audio destination corresponds to an audio device that exists
audio-sink-vs-device-listA resource oracle, and deliberately a different resource from the voice list. - WebSQL (openDatabase) is absent for Chrome ≥ 119
opendatabase-removedWebSQL was removed from Chrome in version 119, so window.openDatabase should not exist on any newer Chromium. - BatteryManager fields satisfy the spec's mutual-exclusion invariants
battery-spec-invariantsThe probe calls navigator.getBattery() once and records four fields together: level, charging, chargingTime, dischargingTime. - mediaCapabilities and canPlayType agree about the same codec
mediacapabilities-vs-canplaytypeTwo different APIs answer from the same platform decoder registry.
Clearcote is a browser built for fingerprint coherence
It is a Chromium fork, maintained by the same people who wrote this reference. It ships as a compiled browser rather than as a stealth script injected into someone else's — which is a description of how it is built, and is not an argument about how it behaves on this check.
This audit takes no position on how Clearcote scores on Environment & locale checks, on this one, or anywhere else. It has no baseline corpus of other people's fingerprints to rank you against and no vendor scoreboard — nearly every check is self-referential, asking one browser the same question through two independent APIs and reporting whether both answers can be true at once. It runs identically on any browser, including ours. Run it on yours and read the result yourself.
See the other checks in Environment & locale — the family media-kinds-vs-engine belongs to.