· deepdives · 7 min read
Unlocking the EyeDropper API: A Beginner's Guide to Color Picking in Web Applications
A practical, beginner-friendly guide to the EyeDropper API: what it does, how to use it, fallbacks for unsupported browsers, accessibility considerations, and example code to integrate color picking into your web apps.

Why color picking matters
Color selection is a common UI need: designers want to sample colors from images, users may want to match brand colors, and editors often need precise pixel-level values. The EyeDropper API gives web apps a native, secure, and simple way to let users pick colors directly from the rendered page.
This tutorial breaks down the API, shows how to use it safely, offers fallback strategies for unsupported browsers, and includes sample code you can drop into your project.
What is the EyeDropper API?
The EyeDropper API is a small browser API that lets web pages present a native color picker that samples a pixel color from the user’s viewport. When used, the user selects a point on the screen and the API returns the sampled color as an sRGB hex string (e.g. #RRGGBB
).
Key characteristics:
- Simple, promise-based API.
- Returns
sRGBHex
(string) on success. - Requires user activation (must be invoked from a user gesture like click).
- Works only in secure contexts (HTTPS) and in top-level browsing contexts.
Browser support & security
Before using EyeDropper in production, check support and constraints:
- Feature detection is simple:
if ('EyeDropper' in window) { ... }
. - Works in many Chromium-based browsers; support evolves - always verify current compatibility.
- Must be called during user activation (click/keyboard activation). Calling it elsewhere will throw.
- Runs only in secure contexts (HTTPS) and top-level browsing contexts.
Check live compatibility details at Can I use: https://caniuse.com/?search=eyedropper and the MDN reference: https://developer.mozilla.org/en-US/docs/Web/API/EyeDropper
Basic usage - simplest example
This is the minimal, modern usage. The call must happen inside a user gesture (e.g., a button click handler).
async function pickColor() {
if (!('EyeDropper' in window)) {
alert('EyeDropper API not available in this browser.');
return;
}
try {
const eyeDropper = new EyeDropper();
const result = await eyeDropper.open();
// result is an object with sRGBHex property, e.g. { sRGBHex: '#aabbcc' }
console.log('Picked color:', result.sRGBHex);
// Do something with the color
} catch (err) {
// User dismissed the tool or an error occurred
console.error('EyeDropper error:', err);
}
}
// Bind to a button
document.querySelector('#pick-color-btn').addEventListener('click', pickColor);
Handling the returned color
The EyeDropper returns a hex string in sRGB. You’ll often want to display a swatch, populate a color input, or convert to other formats (RGBA, HSL) for UI/animation. Here are helper conversions.
function hexToRgba(hex, alpha = 1) {
const normalized = hex.replace('#', '');
const bigint = parseInt(normalized, 16);
const r = (bigint >> 16) & 255;
const g = (bigint >> 8) & 255;
const b = bigint & 255;
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
}
function hexToHsl(hex) {
// Convert hex to HSL; implementation omitted here for brevity - use a tested util in production
}
You can also call navigator.clipboard.writeText(result.sRGBHex)
to copy the hex to clipboard if supported.
UX integration: presenting results to users
- Always show a preview swatch and the color value (hex and an editable input).
- Keep a list of recent picks or a palette so users can reuse colors.
- Respect user gestures and avoid blocking flows - the API already uses a native modal-like experience.
- For touch devices, ensure your UI elements are large enough and the button is easy to tap.
Fallback strategy: when EyeDropper isn’t available
Not all browsers support EyeDropper. Provide sensible fallbacks:
- Offer a standard
<input type="color">
control to let the user manually pick a color. - Implement a canvas-based custom eyedropper: take a snapshot of relevant content (e.g., a displayed image or the entire page rendered to a
Below is a concise fallback implementation pattern (illustrative, simplified):
<!-- Fallback HTML -->
<button id="pick-color-btn">Pick color</button>
<input type="color" id="color-fallback" style="display:none" />
<div id="swatch" style="width:48px;height:48px;border:1px solid #ccc"></div>
<canvas
id="overlay-canvas"
style="position:fixed;inset:0;display:none"
></canvas>
async function pickColorWithFallback() {
if ('EyeDropper' in window) {
try {
const result = await new EyeDropper().open();
applyColor(result.sRGBHex);
return;
} catch (err) {
console.warn('EyeDropper failed or was dismissed', err);
// fall through to fallback
}
}
// Use native color input as a simple fallback
const colorInput = document.querySelector('#color-fallback');
colorInput.style.display = 'inline';
colorInput.focus();
colorInput.click();
colorInput.oninput = e => {
applyColor(e.target.value);
colorInput.style.display = 'none';
};
}
function applyColor(hex) {
document.querySelector('#swatch').style.background = hex;
}
document
.querySelector('#pick-color-btn')
.addEventListener('click', pickColorWithFallback);
For a richer fallback (canvas sampling from images or visible content), you will need to:
- Create a canvas and draw the image (or a capture of content if same-origin).
- Display the canvas as an overlay and track pointer position.
- Use
getImageData(x, y, 1, 1)
to read pixel RGBA values. - Convert the pixel to hex and apply it.
Implementing a full-page snapshot fallback is complex due to cross-origin restrictions (you cannot draw cross-origin images to a canvas and read them), so consider limiting canvas sampling to same-origin images or user-provided uploads.
Accessibility (A11y) & keyboard users
- The EyeDropper is inherently graphical and requires pointer input. Provide alternatives for keyboard-only users: a text input for hex values or a list of pre-defined color swatches.
- Ensure focus management when invoking the EyeDropper: the API is modal-like, but your UI should not lose important state.
- When using a fallback canvas overlay, ensure escape key dismisses the overlay and focus returns to the triggering control.
Best practices and gotchas
- Always feature-detect; do not rely on userAgent sniffing.
- Call
new EyeDropper().open()
only during user activation (click/keydown). Otherwise the promise will reject. - Make a friendly fallback path - many users will still be on unsupported browsers.
- Consider small UI niceties: display the color value in multiple formats, show an accessible label describing the picked color, and offer a copy button.
- For dynamic content (animations, transitions), pause any layout or transitions while the user picks a color so the pixel sampled is predictable.
- Avoid sampling protected or off-limits content (e.g., content in cross-origin iframes) - browsers can impose restrictions to prevent privacy leaks.
Performance & privacy
- The native EyeDropper is lightweight and the browser handles the sampling efficiently.
- If you implement a canvas fallback, avoid doing large, frequent copies of the whole page into a canvas. Instead, restrict the capture area to the image or element of interest.
- Be mindful of privacy: sampling the page could pick up information users wouldn’t expect to share. Make it clear what will be sampled and provide an explicit cancel path.
Advanced tips
- If you need alpha (transparency) information, note the EyeDropper returns an sRGB hex (no alpha). For alpha-aware sampling, use a canvas fallback where you read RGBA.
- Use
navigator.clipboard.writeText()
to let users copy color values easily (requires secure context and clipboard permission/gesture). - To allow keyboard activation, tie the pick action to both click and keydown (Enter/Space) handlers on the trigger element.
Example: Mini color-picking widget
This condensed example shows a button that tries EyeDropper and falls back to a color input. It updates a visible swatch and copies the hex to clipboard.
<button id="pick-btn">Pick color</button>
<div
id="preview"
aria-live="polite"
style="display:flex;gap:12px;align-items:center"
>
<div
id="swatch"
style="width:48px;height:48px;border-radius:6px;border:1px solid #ddd"
></div>
<input id="color-value" type="text" readonly style="width:120px" />
<button id="copy-btn">Copy</button>
</div>
<input type="color" id="fallback-color" style="display:none" />
<script>
async function tryPick() {
const swatch = document.getElementById('swatch');
const colorValue = document.getElementById('color-value');
if ('EyeDropper' in window) {
try {
const { sRGBHex } = await new EyeDropper().open();
swatch.style.background = sRGBHex;
colorValue.value = sRGBHex;
return;
} catch (err) {
console.log('EyeDropper dismissed or error', err);
}
}
// fallback to native color input
const fallback = document.getElementById('fallback-color');
fallback.value = '#ffffff';
fallback.style.display = 'inline';
fallback.focus();
fallback.click();
fallback.oninput = () => {
swatch.style.background = fallback.value;
colorValue.value = fallback.value;
fallback.style.display = 'none';
};
}
document.getElementById('pick-btn').addEventListener('click', tryPick);
document.getElementById('copy-btn').addEventListener('click', async () => {
const value = document.getElementById('color-value').value;
try {
await navigator.clipboard.writeText(value);
alert('Copied ' + value);
} catch (err) {
console.error('Clipboard write failed', err);
}
});
</script>
Debugging & testing tips
- Test on real devices and browsers that your users use. Behavior can differ between desktops and mobile browsers.
- Confirm the API is only called on user gestures. If your code rejects unexpectedly, verify the calling context.
- When implementing canvas fallbacks, watch for cross-origin image tainting errors (reading pixel data from images loaded from other origins will throw).
- Use browser dev tools to catch thrown exceptions and to inspect values returned by
open()
.
When not to use EyeDropper
- If you need alpha channel values from the sampled pixel, EyeDropper won’t provide them. Use a canvas-based approach instead.
- If the content you need to sample lives in a cross-origin iframe or is otherwise protected, you might not be able to sample it for privacy reasons.
Summary
The EyeDropper API is a compact, pragmatic tool that brings native color picking to web applications with minimal code. Use it when you need quick, reliable color sampling from the user’s view. Always feature-detect and provide graceful fallbacks and accessible alternatives for users on unsupported platforms or with different input needs.
References
- MDN Web Docs: EyeDropper API - https://developer.mozilla.org/en-US/docs/Web/API/EyeDropper
- EyeDropper API spec (WICG) - https://wicg.github.io/eyedropper/
- Can I use: compatibility table - https://caniuse.com/?search=eyedropper