# HDR Test

> Is my monitor HDR? dynamic-range vs video-dynamic-range, P3 vs sRGB patches, luminance ladder. OS toggle, panel and browser report are three different things.

- URL: https://bytescope.dev/en/hdr-test/
- Locale: en · Also: [ja](https://bytescope.dev/ja/hdr-test.md) · [zh-Hant](https://bytescope.dev/zh-Hant/hdr-test.md)
- Runs: Nothing is uploaded. The page reads this window's media queries and draws CSS/canvas patterns in this tab.
- Requires: Any modern browser. Whether the samples actually show as HDR depends on the display, the operating system and the browser's HDR video support, which the page reports.
- Updated: 2026-09-08

## What it does

This page answers one question: is the screen in front of you really HDR? Nothing is uploaded. After mount it reads `matchMedia` for `dynamic-range`, `video-dynamic-range` and `color-gamut`, plus `CSS.supports` for wide-gamut colour, then draws a luminance ladder and P3 versus sRGB patches. Drag the window onto another display — the live queries follow.

### Three different things

The OS HDR switch, the panel's peak brightness, and what this browser reports are not one fact. Windows or macOS can have HDR off on a capable panel; a browser can report `dynamic-range: high` on a display whose peak is barely above SDR. This page only sees the browser's report for *this* window.

### dynamic-range vs video-dynamic-range

`(dynamic-range: high)` is the page's output chain. `(video-dynamic-range: high)` is the video plane — a different CSS media feature, not a synonym. A laptop can match one and not the other. They are listed separately on purpose.

### No HDR clip, on purpose

There is no HDR10 sample here. The site's static export is already large enough that a clip would break the Cloudflare deploy, and this environment cannot run `mediainfo` to prove a file actually carries HDR10 metadata. An unverified clip labelled HDR is worse than none. To test playback, open a file you trust in Codec Check or the OS player, or use a known HDR stream. The patterns on this page are CSS and canvas only.

## Limits

Does not calibrate the display or measure peak brightness; it plays HDR samples and reports what the browser detects.

## Machine output

Copy JSON copies an envelope `{ tool, url, generatedAt, userAgentHint, result }`. `tool` is `"hdr-test"`; `result` is what the browser reported via matchMedia / CSS.supports / screen numbers — not a measurement of the attached panel.

- `browserReportsHdr` (boolean | null) — `matchMedia('(dynamic-range: high)')`. `null` = API missing or threw.
- `browserReportsHdrVideo` (boolean | null) — `matchMedia('(video-dynamic-range: high)')`. `null` = API missing or threw.
- `dynamicRangeStandard` (boolean | null) — `matchMedia('(dynamic-range: standard)')`. `null` = API missing or threw.
- `videoDynamicRangeStandard` (boolean | null) — `matchMedia('(video-dynamic-range: standard)')`. `null` = API missing or threw.
- `colorGamutSrgb` (boolean | null) — `matchMedia('(color-gamut: srgb)')`. `null` = API missing or threw.
- `colorGamutP3` (boolean | null) — `matchMedia('(color-gamut: p3)')`. `null` = API missing or threw.
- `colorGamutRec2020` (boolean | null) — `matchMedia('(color-gamut: rec2020)')`. `null` = API missing or threw.
- `inferredColorGamut` (string) — Widest matching color-gamut token: `"srgb"` | `"p3"` | `"rec2020"` | `"unknown"`. `"unknown"` = nothing matched.
- `colorBits8` (boolean | null) — `matchMedia('(color: 8)')` (or equivalent bit-depth query). `null` = API missing or threw.
- `colorBits10` (boolean | null) — 10-bit color query. `null` = API missing or threw.
- `colorBits12` (boolean | null) — 12-bit color query. `null` = API missing or threw.
- `colorDepth` (number | null) — `window.screen.colorDepth`. `null` = not read.
- `devicePixelRatio` (number | null) — `window.devicePixelRatio`. `null` = not read.
- `supportsDisplayP3` (boolean | null) — `CSS.supports` of `color(display-p3 …)` syntax — not the display gamut. `null` = CSS.supports missing or threw.
- `supportsOklch` (boolean | null) — `CSS.supports` of oklch syntax. `null` = CSS.supports missing or threw.
- `supportsRec2020` (boolean | null) — `CSS.supports` of rec2020 color syntax. `null` = CSS.supports missing or threw.
- `browserHdrVerdict` (string) — Fold of the media-query flags: `"hdr"` | `"wide-gamut-only"` | `"sdr"` | `"unknown"`. `"hdr"` means the browser reported `(dynamic-range: high)`, not that the panel was measured as HDR.
- `extendedRangeCssColor` (string | null) — Extended-range `color()` that `CSS.supports` accepted. `null` = none accepted.

## FAQ

### Is my monitor HDR?

This page reports what the *browser* thinks about the window's current output chain. `dynamic-range: high` means the browser believes that chain can do HDR. It does not measure nits, and it does not prove the OS HDR switch is on. Move the window onto another screen if you have two — the answer is per display.

### Why are the OS HDR switch, the panel, and the browser three different things?

The panel is hardware: peak brightness, local dimming, whether it can even do PQ or HLG. The OS switch decides whether the desktop is in an HDR mode. The browser then reports media queries for *this* window. Any one of the three can disagree with the other two. A yes from this page is the browser's yes, not a calorimeter reading.

### What does dynamic-range: high actually mean?

`dynamic-range: high` means the browser believes the current output chain supports HDR. It does not mean the panel's peak is high enough to look impressive, and it does not mean SDR content will suddenly look better. `(dynamic-range: standard)` also matches HDR displays, because they can still show SDR — it is not an SDR-only flag.

### Why is there no HDR video sample?

Two reasons, both practical. The exported site is already ~275 MB, and a previous deploy had to move a WASM runtime to a CDN to fit Cloudflare; shipping HDR video would make the deploy fail. And `mediainfo` is not installed here, so a bundled clip could not be verified to carry HDR10 metadata. Play an HDR file you already have, or a known HDR stream. This page will not pretend a random mp4 is HDR10.

### Why did the numbers change when I moved the window?

Every `matchMedia` query is subscribed with `change`. The window's output chain is the one that matters, so dragging onto another monitor is supposed to update gamut, dynamic range and bits-per-channel. That is the point of the page, not a glitch.

### The P3 and sRGB patches look the same. Is my screen not wide-gamut?

If they match, this window is likely limited to sRGB: either the panel is sRGB, the OS is not in a wide-gamut mode, or the browser is not sending Display P3. A difference in the reds and greens is the wide-gamut signal. `CSS.supports('color', 'color(display-p3 1 0 0)')` only means the engine parses the syntax — it is not a display measurement.

## Related tools

- [Display & Monitor Test](https://bytescope.dev/en/screen-test.md) — Dead pixels, banding, ghosting, measured refresh rate — same family.
- [Codec Check](https://bytescope.dev/en/codec-check.md) — Can this browser decode HEVC, AV1, VP9 — including 10-bit HDR configs.
- [My Computer Specs](https://bytescope.dev/en/my-computer-specs.md) — See which GPU, how many CPU threads and how much RAM this browser reports, and whether hardware acceleration is on.
