Color Gamut, HDR, and Media-Query Fingerprinting: What Your Display Reveals
If you test sites across device types from a desktop, or run several profiles on different display configurations, there is a fingerprint surface you probably haven't checked. This tutorial covers what CSS media features expose, why they come from the real display, and how to test whether a profile's signals agree with each other.
See your own values first. We built a public checker at justbrowser.app/fingerprint-checker, client-side with no signup. It compares your WebRTC candidates against your public IP and checks whether the IP's location has a UTC offset that agrees with your browser's timezone. Worth running before you read the rest of this, so the signals below have your numbers attached to them.
What We're Building
By the end of this walkthrough, you'll understand:
- Which CSS media features create fingerprinting surfaces (color-gamut, dynamic-range, prefers-color-scheme, prefers-reduced-motion, forced-colors)
- How
screen.colorDepthcombines with media queries to identify display classes - Why a claimed device and the real display can disagree on these signals
- How to test your profiles for inconsistent media query results
- Configuration approaches that keep a profile consistent
This is the display fingerprint surface that sits next to screen resolution — which we covered in the screen resolution and window metrics guide. Resolution is about size. This is about color capability.
Prerequisites
- Chrome or Chromium-based browser (any recent version)
- A multi-account browser with configured profiles — JustBrowser gives you the full feature set on a 7-day trial (card required, no charge if you cancel within the seven days)
- Basic familiarity with CSS media queries (you don't need to be an expert, but you should know what
@mediadoes) - Dev tools open
Step 1: Understanding What Media Queries Expose
Open your browser console and paste this:
function getMediaFingerprint() {
const checks = {
colorGamut: {
srgb: matchMedia('(color-gamut: srgb)').matches,
p3: matchMedia('(color-gamut: p3)').matches,
rec2020: matchMedia('(color-gamut: rec2020)').matches
},
dynamicRange: {
standard: matchMedia('(dynamic-range: standard)').matches,
high: matchMedia('(dynamic-range: high)').matches
},
colorScheme: {
dark: matchMedia('(prefers-color-scheme: dark)').matches,
light: matchMedia('(prefers-color-scheme: light)').matches
},
reducedMotion: matchMedia('(prefers-reduced-motion: reduce)').matches,
forcedColors: matchMedia('(forced-colors: active)').matches,
colorDepth: screen.colorDepth,
pixelDepth: screen.pixelDepth
};
console.log('Display Fingerprint:', checks);
return checks;
}
getMediaFingerprint();
On a standard sRGB monitor, the output looks like this:
Display Fingerprint: {
colorGamut: { srgb: true, p3: false, rec2020: false },
dynamicRange: { standard: true, high: false },
colorScheme: { dark: true, light: false },
reducedMotion: false,
forcedColors: false,
colorDepth: 24,
pixelDepth: 24
}
On a wide-gamut HDR display, such as a recent MacBook Pro screen in Chrome, you'd typically see:
Display Fingerprint: {
colorGamut: { srgb: true, p3: true, rec2020: false },
dynamicRange: { standard: true, high: true },
colorScheme: { dark: true, light: false },
reducedMotion: false,
forcedColors: false,
colorDepth: 30,
pixelDepth: 30
}
See the difference? P3 gamut support. HDR. 30-bit color depth. These values come from the actual display hardware.
That is the point to watch: if a profile presents a MacBook Pro identity but the actual display only supports sRGB at 24-bit, the profile contradicts itself, and any script comparing the two can see it. The same goes for phone emulation on a desktop: the user agent and viewport change, but color-gamut and dynamic-range still come from your monitor. For mobile display testing, use a real device.
Step 2: Why This Comes From the Real Display
Canvas fingerprinting happens in JavaScript. Canvas, WebGL, audio context and font enumeration are all JavaScript surfaces.
Media queries work differently.
When a site checks matchMedia('(color-gamut: p3)'), the browser queries the operating system's display capabilities. The OS asks the display hardware. The result bubbles back up, and the CSS engine uses the same answer when it evaluates stylesheets, with no JavaScript involved.
A site can do this:
@media (color-gamut: p3) {
.fingerprint-pixel { background-image: url('/pixel/p3.gif'); }
}
@media (color-gamut: srgb) and (not (color-gamut: p3)) {
.fingerprint-pixel { background-image: url('/pixel/srgb.gif'); }
}
No JavaScript executes. The browser fetches one URL or the other based on actual display capabilities. The server logs which file was requested. Your display class is fingerprinted purely through CSS.
A browser extension works in JavaScript, so it cannot change what the CSS engine reports. Settings that live inside the engine can. JustBrowser sets screen dimensions, device pixel ratio and color depth in C++, so screen.* and the device-width/device-height media features agree. Color-gamut and dynamic-range follow the host display today, so keep profiles on a display class that matches the host (sRGB/SDR on a standard monitor). Our best practices checklist covers where native and extension-based approaches each stop.
Step 3: The Display Class Fingerprint
Quick entropy math.
Individual media features don't uniquely identify you. But combined:
- Color gamut: 3-4 classes (srgb-only, p3, rec2020)
- Dynamic range: 2 classes (standard, high)
- Color depth: 3 common values (24, 30, 48)
- Color scheme: 2 states (dark, light)
- Reduced motion: 2 states
- Forced colors: 2 states
That's 3 × 2 × 3 × 2 × 2 × 2 = 144 combinations just from display-related signals. Not enough to uniquely identify anyone — but enough to create device classes. And that's the whole game, really. Device classes.
Real fingerprinting combines display signals with everything else: canvas, WebGL, fonts, audio, TLS fingerprint, screen dimensions, timezone. The WebGL and GPU fingerprinting guide shows how these vectors stack.
Display signals matter most for consistency checking. A profile that presents a wide-gamut laptop should have that laptop's display profile. If it doesn't match, the profile contradicts itself.
The common oversight is configuring screen resolution and device pixel ratio but forgetting color capabilities, which gives a profile that's dimensionally right but chromatically wrong. When you're setting up many profiles for QA, device testing or client work, color gamut is easy to overlook. You're thinking about proxies and cookies. But fingerprinting scripts read display signals too, and a mismatch makes your test results unrepresentative.
Step 4: Testing Your Profiles for Inconsistent Media Query Results
Here's a quick audit.
Step 4a: Check JavaScript media query response
const isP3 = matchMedia('(color-gamut: p3)').matches;
const isHDR = matchMedia('(dynamic-range: high)').matches;
const depth = screen.colorDepth;
console.log(`P3: ${isP3}, HDR: ${isHDR}, Depth: ${depth}`);
Step 4b: Check what your profile reports
If your profile is configured as:
- MacBook Pro (2021+): should report P3 + HDR + 30-bit
- Standard Windows laptop: should report sRGB + standard + 24-bit
- Gaming monitor with HDR: varies, but likely sRGB/P3 + HDR + 30-bit
Mismatch? The profile's display signals disagree with its reported device.
Step 4c: Check the CSS path
This is trickier. You need a server-side test. For a quick check:
- Open BrowserLeaks.com's CSS section
- Check what display capabilities they detect
- Compare against your profile configuration
Alternatively, set up a local test page with CSS background-image rules that load different pixels based on media queries, then check your server logs. If the requested image doesn't match the display class the profile reports, the CSS path is showing the real display.
Step 4d: Check screen.colorDepth alignment
Color depth should match gamut capability:
- sRGB displays: usually 24-bit (8 bits per channel × 3)
- P3/HDR displays: usually 30-bit (10 bits per channel × 3) or sometimes 48-bit
A profile reporting 30-bit depth but sRGB-only gamut is worth a second look. That combination exists on some displays, but it's uncommon.
Step 5: Configuration Approaches That Keep Profiles Consistent
Option 1: Use a multi-account browser with native screen-level settings
JustBrowser sets screen.colorDepth and screen geometry at the engine level. Gamut/HDR media queries follow the host display, so match the profile's reported display class to the machine it runs on.
Option 2: Match profiles to your actual hardware
If you're running all profiles on the same desktop, configure them all for your actual display class. Don't present a wide-gamut identity, such as a recent MacBook, from an sRGB monitor.
This limits your device diversity, but a consistent profile is more useful for testing than an inconsistent one.
Option 3: Use hardware that matches target profiles
Some teams run mobile tests on actual mobile devices, or use MacBooks for P3/HDR profiles. The hardware matches the reported capabilities.
Expensive and operationally complex, and overkill for most people.
Common Errors and How to Fix Them
Error: "Profile presents a MacBook but shows sRGB gamut"
Cause: Your monitor only supports sRGB. The profile sets screen size, color depth and device pixel ratio, but the gamut and dynamic-range media queries follow the host display.
Fix: Switch to profiles that match your actual display capabilities.
Error: "screen.colorDepth is 24 but profile targets HDR device"
Cause: HDR devices report 30-bit or higher color depth. Your profile reports an HDR device but the color depth shows your real display.
Fix: Check that screen.colorDepth and the media queries agree with the device the profile reports.
Error: "prefers-color-scheme doesn't match OS setting"
Cause: The OS-level setting reaches CSS even when the profile's JavaScript-visible setting differs.
Fix: Less critical than gamut/HDR for device classification, but still a consistency signal. Accept your OS setting, or change it at the OS level for the test run.
Error: "Common checkers look fine but the results still look inconsistent"
Cause: Public checkers focus on common vectors. Other scripts may query less-common surfaces like forced-colors or reduced-motion.
Fix: Check all media query surfaces manually with the code snippets above, and compare results across more than one checker.
Next Steps
Media queries are one display surface among several. If you've got this handled:
- Screen resolution and window metrics — the dimension mismatch guide covers the other half of display fingerprinting
- Audio and font fingerprinting — two more hardware-linked surfaces. The audio and font fingerprinting guide shows how they interconnect with display signals.
- TLS fingerprinting — JA4 fingerprints are device-class signals too. The JA4 TLS guide covers how sites read it.
For the broader multi-account setup, our best practices checklist runs through the full surface. And if you're running campaigns that need click-quality monitoring, ClickzProtect covers that side. For analytics on your own site, JustAnalytics provides session tracking without third-party fingerprinting scripts.
Display fingerprinting through media queries isn't the highest-entropy surface. But it is where a claimed device and the real display most visibly disagree. A five-minute check will catch this before it skews your test results.
Frequently Asked Questions
What CSS media features expose fingerprinting signals?
The main fingerprinting surfaces are color-gamut (srgb, p3, rec2020), dynamic-range (standard vs high for HDR), prefers-color-scheme (dark/light mode), prefers-reduced-motion (accessibility setting), and forced-colors (high contrast mode). Combined with screen.colorDepth and window.matchMedia() behavior, these create a display-class fingerprint that varies between device types.
Why do media queries expose a mismatch between a claimed device and the real display?
Displays differ in what they can show. Most standard desktop monitors report srgb gamut and standard dynamic range, while wide-gamut screens such as recent MacBook Pro displays and newer phones report p3 gamut and high dynamic range. A profile that presents a MacBook Pro identity but reports srgb gamut and 24-bit color depth is internally inconsistent, and fingerprinting services compare these values against expected hardware configurations.
Can CSS-only fingerprinting read my display without JavaScript?
Yes. Sites can use CSS-only fingerprinting by loading different resources based on media query matches. No JavaScript required. A CSS rule like @media (color-gamut: p3) loads one image, @media (color-gamut: srgb) loads another. The server sees which resource was requested and knows your display class without any client-side code executing.
How do I check that my profile reports media queries consistently?
Run window.matchMedia('(color-gamut: p3)').matches in the console, along with checks for dynamic-range and prefers-color-scheme. Compare the results against the device the profile reports. A profile presenting a recent MacBook Pro should show p3 gamut and high dynamic range. Check screen.colorDepth too: in Chrome it is typically 30 on wide-gamut HDR displays and 24 on standard ones.
Try JustBrowser
A multi-account browser for teams, built on a custom Chromium engine with native C++ integration — not extension-based. Each profile keeps its own cookies, storage, proxy and 40+ identity parameters, and teammates can share profiles. Local REST API + CDP for Playwright, Puppeteer, Selenium. $9.99/month or $99.99/year. 7-day free trial, card required — cancel any time in the seven days and you are not charged. Unlimited profiles.
Get started → · How it differs from Multilogin / GoLogin / AdsPower
Ready to manage multiple accounts?
Seven days free, then $9.99/month — one plan, everything included.