# EyeDropper API: sampling on-screen colors

> How the EyeDropper() constructor and open() method let a page sample a pixel's color anywhere on screen for a custom color picker, why open() requires a user gesture, and the desktop-only, Chromium-only support starting at Chrome and Edge 95.

**In one line:** per Chrome for Developers, "the EyeDropper API enables authors to use a
browser-supplied eyedropper in the construction of custom color pickers," letting a page
sample the color of any on-screen pixel — including pixels outside the browser window —
without shipping its own color-sampling code.

## Creating and opening an eyedropper

Per Chrome for Developers, `open()` "can only be called in response to a user action
(like a button click)," so the call belongs inside the click handler itself — pasting it
at the top level of a script will not start the eyedropper:

```js
const pickButton = document.createElement("button");
pickButton.textContent = "Pick color";
const colorInput = document.createElement("input");
colorInput.type = "color";
document.body.append(pickButton, colorInput);

pickButton.addEventListener("click", async () => {
  const eyeDropper = new EyeDropper();
  try {
    const result = await eyeDropper.open();
    colorInput.value = result.sRGBHex; // e.g. "#3366ff"
  } catch (err) {
    // Rejects if the user cancels the pick, e.g. by pressing Escape.
    console.log("Eyedropper pick cancelled:", err);
  }
});
```

The `open()` method, per Chrome for Developers, "returns a promise that resolves after the
user selects a pixel on the screen," and the resolved value gives "access to the pixel's
color in sRGBHex format (`#RRGGBB`)." The promise rejects when the user cancels instead
of picking a pixel; the example handles that rejection with `try`/`catch`.

## User-gesture requirement

Chrome for Developers is direct about the activation requirement: "the API doesn't
actually let the eyedropper mode start without user intent. The `open()` method can only
be called in response to a user action (like a button click)." Calling `open()` outside a
click or similar user gesture handler will not start the eyedropper.

## Cancelling an open eyedropper

`open()` also accepts an `AbortSignal` so an in-progress pick can be cancelled
programmatically, in addition to the user pressing Escape:

```js
const controller = new AbortController();
eyeDropper.open({ signal: controller.signal }).catch(() => {
  // Rejects if the user cancels (e.g. presses Escape) or the signal aborts.
});
// Elsewhere, e.g. on a "Cancel" button:
controller.abort();
```

## Feature detection and fallback

`"EyeDropper" in window` is `false` in every browser that doesn't implement the API
(Firefox, Safari). The fallback branch below actually shows the `<input type="color">`
swatch instead of the pick button, rather than just noting that it should:

```js
function initColorPicker(pickButton, colorInput) {
  if (!("EyeDropper" in window)) {
    // No native eyedropper here — hide the pick button and let users pick a color
    // with the always-present <input type="color"> swatch instead.
    pickButton.hidden = true;
    colorInput.hidden = false;
    return;
  }
  pickButton.hidden = false;
  pickButton.addEventListener("click", async () => {
    const eyeDropper = new EyeDropper();
    try {
      const { sRGBHex } = await eyeDropper.open();
      colorInput.value = sRGBHex;
    } catch (err) {
      // Rejects if the user cancels the pick, e.g. by pressing Escape.
      console.log("Eyedropper pick cancelled:", err);
    }
  });
}

const pickButton = document.createElement("button");
pickButton.textContent = "Pick color";
const colorInput = document.createElement("input");
colorInput.type = "color";
colorInput.hidden = true;
document.body.append(pickButton, colorInput);
initColorPicker(pickButton, colorInput);
```

## Where it is supported

Per MDN, the EyeDropper API "is not Baseline because it does not work in some of the most
widely-used browsers." Per Chrome for Developers, the API is supported "on Chromium-based
browsers like Edge or Chrome as of version 95"; per caniuse's tracking of the feature,
support also extends to Opera. Chrome for Developers' own walkthrough runs the demo "on
Windows or Mac," consistent with the API being desktop-only in Chromium's implementation.
Firefox and Safari do not implement the EyeDropper API.

## Practical checklist

- [ ] Feature-detect `"EyeDropper" in window` before constructing one — support is limited
      to Chromium desktop browsers (Chrome/Edge 95+), per MDN and Chrome for Developers.
- [ ] Only call `open()` from inside a user-gesture handler (e.g. a click listener) — per
      Chrome for Developers, it "can only be called in response to a user action."
- [ ] Handle the rejected promise from `open()` — it rejects if the user cancels the pick
      (for example by pressing Escape) instead of selecting a pixel.
- [ ] Pass an `AbortSignal` if your UI needs to cancel an open eyedropper programmatically,
      such as when the picker's host dialog is dismissed.
- [ ] Provide a fallback color input (e.g. `<input type="color">`) for browsers without
      `EyeDropper`, since Firefox and Safari do not implement it.

## Cross-references

- [Async Clipboard API](/reference/capabilities/clipboard/)
- [Screen Wake Lock API](/reference/capabilities/wake-lock/)