iKit
Technical · 9 min read ·

EyeDropper API: Sample Any Pixel on Screen in 2026

How the EyeDropper API lets a web app sample any pixel on the screen, the exact JavaScript to call it, and the fallback you still need in 2026.

EyeDropper API: Sample Any Pixel on Screen in 2026

EyeDropper API: Sample Any Pixel on Screen From the Browser

The EyeDropper API gives a web app something that was impossible for years: a native tool that samples the color of any pixel on the user's screen, including pixels rendered outside the browser tab. You open it from a click, the cursor turns into a magnifier, the user clicks a pixel, and you get back a hex string. This post covers the exact JavaScript, the browser-support reality in 2026, and the fallback you still have to ship.

TL;DR

  • new EyeDropper().open() returns a promise resolving to { sRGBHex }.
  • Chromium desktop only — Chrome 95+ and Edge 95+; no Firefox, Safari, or mobile.
  • open() requires a user gesture and a secure (HTTPS) context.
  • Always feature-detect with 'EyeDropper' in window and ship a fallback.
  • The API returns hex only; convert to RGB, HSL, or OKLCH yourself.

What is the EyeDropper API?

Creative desktop apps have had eyedroppers forever. Photoshop samples a color off the canvas; PowerPoint pulls a fill color from anywhere on screen; even Chrome DevTools has one in the CSS color editor. On the web, none of that was possible without hacks. The closest native option was the <input type="color"> picker, which on Chromium desktop hides an eyedropper inside its dropdown — but you can't style it, script it cleanly, or reach it in other browsers.

The gap it fills

The EyeDropper API closes that gap by exposing the browser's own screen-sampling tool directly. According to the MDN EyeDropper API reference, the tool can sample colors "including outside of the browser window" — that's the part <input type="color"> never gave you programmatically.

What it returns

Per the WICG specification, open() resolves to a ColorSelectionResult dictionary with exactly one member: sRGBHex, a valid simple color string in #RRGGBB form. No alpha, no named channels, no color space choice. One hex string per pick.

Who shipped it

The API was specified and implemented by Ionel Popescu of the Microsoft Edge team, which is why Edge and Chrome shipped it in the same release. It lives in the WICG (Web Incubator Community Group), not yet in a W3C Recommendation, so it's still officially experimental.

How to use the EyeDropper API in JavaScript

The surface area is tiny: one constructor, one method. The important details are the promise flow and the two things the browser enforces — a user gesture and a secure context.

Feature detection first

Never call the constructor blind. It doesn't exist in Firefox or Safari, so touching it throws. Guard with a simple in check:

if ('EyeDropper' in window) {
  // API is available
} else {
  // show your fallback picker
}

Opening the eyedropper

Create an EyeDropper instance and call open() inside a click handler. It returns a promise that resolves when the user clicks a pixel and rejects when they hit Escape:

const btn = document.querySelector('#pick');

btn.addEventListener('click', async () => {
  const dropper = new EyeDropper();
  try {
    const { sRGBHex } = await dropper.open();
    console.log('picked', sRGBHex); // "#4f46e5"
  } catch (err) {
    // user pressed Escape — not an error
  }
});

Two rules the browser enforces here. First, open() needs transient activation — call it outside a real user gesture and the promise rejects with NotAllowedError. Second, the interface is SecureContext, so it only exists on HTTPS (or localhost). On plain HTTP, window.EyeDropper is undefined.

Cancelling with AbortController

If your app state changes mid-pick — a modal opens, the user navigates away — you can cancel the eyedropper yourself with an AbortSignal:

const ctrl = new AbortController();

dropper
  .open({ signal: ctrl.signal })
  .then(r => console.log(r.sRGBHex))
  .catch(() => {}); // aborted or escaped

// later, to force-close it:
ctrl.abort();

One more spec detail worth knowing: only one eyedropper can be open at a time. Calling open() while another is active rejects with InvalidStateError. Track your own open state if you wire the tool to multiple buttons.

Why does the EyeDropper API only work in Chrome and Edge?

This is the question that decides your architecture. The API is Chromium-only, and there's no sign of that changing in 2026.

The support matrix

Here's where things stand on desktop, per the Chrome for Developers guide and the caniuse support table:

Browser Support
Chrome 95+ (desktop)
Edge 95+ (desktop)
Firefox Not supported
Safari Not supported

Mobile is a flat no across the board — neither Chrome for Android nor Safari on iOS exposes it, because there's no on-screen cursor metaphor to sample with on touch.

Why Firefox and Safari haven't shipped

Both projects have open position requests but no implementation. MDN flags the feature as not Baseline precisely because it's missing from widely-used engines. Treat it as a progressive enhancement, never a hard dependency.

What this means in practice

  • Roughly two-thirds of desktop users get the native picker.
  • Every Firefox, Safari, and mobile user needs a fallback.
  • You cannot detect support by browser name — feature-detect at runtime.

How to build a fallback color picker when EyeDropper is missing

Because coverage is partial, a shippable feature branches on detection. The good news: the fallback is a one-line native element that every browser has supported for a decade.

Branch on feature detection

Use the native <input type="color"> when the API is absent. It won't sample outside the page, but it gives users a working picker everywhere:

async function pickColor() {
  if ('EyeDropper' in window) {
    const { sRGBHex } = await new EyeDropper().open();
    return sRGBHex;
  }
  // fallback: native color input
  return new Promise(resolve => {
    const input = document.createElement('input');
    input.type = 'color';
    input.addEventListener('input', () =>
      resolve(input.value)
    );
    input.click();
  });
}

Convert the hex you get back

Both paths hand you a hex string, but design systems usually want RGB, HSL, or OKLCH. The EyeDropper API won't convert for you. Drop the value into a color converter to get every format at once, or pipe several samples into a color palette generator to build a scheme from what you picked off the screen. If you're sampling two ends of a scale, a gradient generator turns two picked stops into a ready CSS gradient.

Store and reuse samples

A common pattern is a "recent colors" strip. Keep an array of the hex strings you collected, dedupe, and render swatches. Since the API returns plain #RRGGBB, they slot straight into CSS custom properties with no parsing:

const recent = [];
function remember(hex) {
  if (!recent.includes(hex)) recent.unshift(hex);
  document.documentElement.style
    .setProperty('--last-pick', hex);
}

Security and privacy: can a website read my screen?

An API that reads arbitrary screen pixels sounds alarming. The spec is built around making silent screen-scraping impossible, and it's worth understanding the four guardrails.

The four guardrails

The WICG spec's security section and MDN both spell these out:

  • User gesture required. open() only runs from a real interaction like a click.
  • No background reads. The promise resolves only after the user clicks a pixel — moving the mouse leaks nothing.
  • Obvious mode. The normal cursor disappears and a magnifier appears, with a deliberate delay so the user notices before any pick is possible.
  • Always cancellable. Escape exits the mode, and the page can't suppress that.

Why the delay matters

That enforced delay between opening the tool and allowing a selection isn't a UX afterthought — it's a defense. It guarantees the user sees the eyedropper UI before any pixel can be captured, so a page can't flash the tool open and closed to grab a color the user never meant to share.

It's still local-only

The color never leaves the device unless your app sends it somewhere. Sampling happens entirely in the browser, which is the same privacy posture as every tool on iKit — the pixels stay on the user's machine. That's a genuine selling point when your users are pulling brand colors off confidential mockups.

References

Related on iKit

Related posts