AI Color Picker Chrome Extension: A Developer Guide
Color selection has evolved beyond simple hex code pickers. Modern AI-powered color picker extensions bring intelligent color suggestions, palette generation, and accessibility analysis directly into your browser workflow. This guide covers the technical implementation and practical applications for developers and power users building or using these tools.
How AI Color Pickers Differ from Traditional Tools
Traditional color pickers give you a visual interface to select colors—they capture pixel data from the screen and convert it to hex, RGB, or HSL values. AI color pickers extend this core functionality by analyzing color relationships, suggesting complementary shades, checking contrast ratios for accessibility compliance, and generating harmonious palettes based on context.
The technical foundation involves three components: screen capture for color extraction, machine learning models for color analysis, and a user interface for displaying results and suggestions.
Setting Up Your Extension
A basic AI color picker extension requires Manifest V3 configuration with specific permissions for screen capture and storage:
{
"manifest_version": 3,
"name": "AI Color Picker",
"version": "1.0.0",
"permissions": [
"activeTab",
"scripting",
"storage"
],
"host_permissions": [
"<all_urls>"
],
"action": {
"default_popup": "popup.html"
},
"background": {
"service_worker": "background.js"
}
}
The key difference from traditional color pickers is the inclusion of AI processing logic, which can run locally using WebAssembly models or connect to external AI services for more sophisticated analysis.
Implementing Color Capture
The core functionality starts with capturing colors from the webpage. Use the chrome.scripting.executeScript API to inject a color extraction script:
// background.js
chrome.action.onClicked.addListener(async (tab) => {
const results = await chrome.scripting.executeScript({
target: { tabId: tab.id },
function: extractColorsFromPage
});
const colors = results[0].result;
processColorsWithAI(colors);
});
function extractColorsFromPage() {
const colors = new Map();
// Get colors from computed styles
const elements = document.querySelectorAll('*');
elements.forEach(el => {
const style = window.getComputedStyle(el);
const bgColor = style.backgroundColor;
const textColor = style.color;
if (bgColor && bgColor !== 'rgba(0, 0, 0, 0)') {
colors.set(bgColor, (colors.get(bgColor) || 0) + 1);
}
if (textColor) {
colors.set(textColor, (colors.get(textColor) || 0) + 1);
}
});
// Sort by frequency and return top colors
return Array.from(colors.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 20)
.map(([color]) => color);
}
This script extracts the dominant colors from a page, giving your AI model context about the current design palette.
Building the Color Analysis Engine
The AI component analyzes extracted colors to provide meaningful suggestions. Here’s a practical implementation using local processing:
// color-analyzer.js
class ColorAnalyzer {
constructor() {
this.contrastThreshold = 4.5; // WCAG AA for normal text
}
// Convert hex to RGB
hexToRgb(hex) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
// Calculate relative luminance
getLuminance(r, g, b) {
const [rs, gs, bs] = [r, g, b].map(c => {
c = c / 255;
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
});
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
}
// Check contrast ratio between two colors
getContrastRatio(color1, color2) {
const rgb1 = this.hexToRgb(color1);
const rgb2 = this.hexToRgb(color2);
if (!rgb1 || !rgb2) return 0;
const l1 = this.getLuminance(rgb1.r, rgb1.g, rgb1.b);
const l2 = this.getLuminance(rgb2.r, rgb2.g, rgb2.b);
const lighter = Math.max(l1, l2);
const darker = Math.min(l1, l2);
return (lighter + 0.05) / (darker + 0.05);
}
// Generate complementary colors
getComplementary(hex) {
const rgb = this.hexToRgb(hex);
if (!rgb) return null;
return this.rgbToHex(
255 - rgb.r,
255 - rgb.g,
255 - rgb.b
);
}
// Generate analogous colors
getAnalogous(hex) {
const hsl = this.hexToHsl(hex);
return [
this.hslToHex((hsl.h + 30) % 360, hsl.s, hsl.l),
this.hslToHex((hsl.h - 30 + 360) % 360, hsl.s, hsl.l)
];
}
// Analyze color for accessibility
analyzeAccessibility(foreground, background) {
const ratio = this.getContrastRatio(foreground, background);
return {
ratio: ratio.toFixed(2),
passesAA: ratio >= 4.5,
passesAAA: ratio >= 7.0,
suggestion: ratio < 4.5 ?
'Consider using a darker foreground or lighter background' :
'Passes WCAG AA requirements'
};
}
}
This analyzer provides core functionality that power users need: contrast checking, palette generation, and accessibility validation.
Color Format Conversion Utilities
Supporting multiple color formats requires conversion utilities. Here’s a comprehensive set covering HEX, RGB, and HSL:
// color-utils.js - Color Conversion Functions
const ColorUtils = {
hexToRgb(hex) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
},
rgbToHsl(r, g, b) {
r /= 255; g /= 255; b /= 255;
const max = Math.max(r, g, b), min = Math.min(r, g, b);
let h, s, l = (max + min) / 2;
if (max === min) {
h = s = 0;
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = ((g - b) / d + (g < b ? 6 : 0)) / 6; break;
case g: h = ((b - r) / d + 2) / 6; break;
case b: h = ((r - g) / d + 4) / 6; break;
}
}
return { h: Math.round(h * 360), s: Math.round(s * 100), l: Math.round(l * 100) };
},
generatePalette(baseColor, count = 5) {
const rgb = this.hexToRgb(baseColor);
if (!rgb) return [];
const hsl = this.rgbToHsl(rgb.r, rgb.g, rgb.b);
const palette = [];
for (let i = 0; i < count; i++) {
const newL = Math.max(10, Math.min(90, hsl.l + (i - 2) * 15));
palette.push(`hsl(${hsl.h}, ${hsl.s}%, ${newL}%)`);
}
return palette;
}
};
Storing Color History
Persisting color history improves workflow efficiency. The Chrome Storage API handles this cleanly:
// storage.js - Color History Management
const ColorHistory = {
STORAGE_KEY: 'color_picker_history',
MAX_HISTORY: 20,
async addColor(color) {
const history = await this.getHistory();
const filtered = history.filter(c => c !== color);
filtered.unshift(color);
const trimmed = filtered.slice(0, this.MAX_HISTORY);
return new Promise((resolve) => {
chrome.storage.local.set({ [this.STORAGE_KEY]: trimmed }, resolve);
});
},
async getHistory() {
return new Promise((resolve) => {
chrome.storage.local.get(this.STORAGE_KEY, (result) => {
resolve(result[this.STORAGE_KEY] || []);
});
});
}
};
The implementation limits history to 20 colors to prevent unbounded storage growth while maintaining useful recent selections.
Creating the User Interface
The popup interface should provide quick access to captured colors and AI suggestions:
<!-- popup.html -->
<!DOCTYPE html>
<html>
<head>
<style>
body { width: 320px; padding: 16px; font-family: system-ui; }
.color-swatch {
width: 40px; height: 40px;
border-radius: 8px;
display: inline-block;
margin: 4px;
cursor: pointer;
border: 2px solid transparent;
}
.color-swatch:hover { transform: scale(1.1); }
.color-swatch.selected { border-color: #0066cc; }
.color-info { margin: 12px 0; padding: 12px; background: #f5f5f5; border-radius: 8px; }
.color-value { font-family: monospace; font-size: 14px; }
.contrast-result { margin-top: 8px; padding: 8px; border-radius: 4px; }
.pass { background: #d4edda; color: #155724; }
.fail { background: #f8d7da; color: #721c24; }
</style>
</head>
<body>
<h3>AI Color Picker</h3>
<div id="colors"></div>
<div id="analysis"></div>
<script src="popup.js"></script>
</body>
</html>
// popup.js
const analyzer = new ColorAnalyzer();
document.addEventListener('DOMContentLoaded', async () => {
// Get colors from storage (set by background script)
const result = await chrome.storage.local.get('extractedColors');
const colors = result.extractedColors || [];
const container = document.getElementById('colors');
colors.slice(0, 12).forEach((color, index) => {
const swatch = document.createElement('div');
swatch.className = 'color-swatch';
swatch.style.backgroundColor = color;
swatch.dataset.color = color;
swatch.addEventListener('click', () => {
selectColor(color);
document.querySelectorAll('.color-swatch').forEach(s => s.classList.remove('selected'));
swatch.classList.add('selected');
});
container.appendChild(swatch);
});
});
function selectColor(color) {
const analysis = document.getElementById('analysis');
const rgb = analyzer.hexToRgb(color);
// Generate palette suggestions
const complementary = analyzer.getComplementary(color);
const analogous = analyzer.getAnalogous(color);
analysis.innerHTML = `
<div class="color-info">
<div class="color-value">HEX: ${color}</div>
<div class="color-value">RGB: ${rgb.r}, ${rgb.g}, ${rgb.b}</div>
</div>
<h4>Suggested Palette</h4>
<div class="color-swatch" style="background-color: ${complementary}"
title="Complementary"></div>
${analogous.map(c => `<div class="color-swatch" style="background-color: ${c}" title="Analogous"></div>`).join('')}
`;
}
Advanced Features for Power Users
Beyond basic color picking, advanced extensions offer these capabilities:
Color naming — Use AI to describe colors in human-readable terms. Train a small model to recognize thousands of color names from CSS color keywords plus extended palettes.
Palette extraction from images — Extract dominant colors from any image on the page using color quantization algorithms:
// Extract palette from images on page
function extractFromImages() {
const images = document.querySelectorAll('img');
const palettes = [];
images.forEach(img => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Sample at small size for performance
canvas.width = 50;
canvas.height = 50;
if (img.complete) {
ctx.drawImage(img, 0, 0, 50, 50);
const data = ctx.getImageData(0, 0, 50, 50).data;
// Simple color quantization
const colorCounts = {};
for (let i = 0; i < data.length; i += 4) {
const r = Math.round(data[i] / 32) * 32;
const g = Math.round(data[i + 1] / 32) * 32;
const b = Math.round(data[i + 2] / 32) * 32;
const key = `rgb(${r},${g},${b})`;
colorCounts[key] = (colorCounts[key] || 0) + 1;
}
palettes.push(colorCounts);
}
});
return palettes;
}
Export capabilities — Allow users to export palettes in multiple formats:
function exportPalette(colors, format) {
switch (format) {
case 'css':
return `:root {\n${colors.map((c, i) => ` --color-${i + 1}: ${c};`).join('\n')}\n}`;
case 'json':
return JSON.stringify(colors.map(c => ({ hex: c })), null, 2);
case 'scss':
return colors.map((c, i) => `$color-${i + 1}: ${c};`).join('\n');
default:
return colors.join(', ');
}
}
Popup UI Styles and Input Handling
A polished popup requires thoughtful CSS and a clean input handler. Here’s a practical baseline:
/* popup.css - Color Picker Styles */
.color-picker-container {
width: 280px;
padding: 16px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
.color-preview {
width: 100%;
height: 60px;
border-radius: 8px;
border: 1px solid #ddd;
margin-bottom: 16px;
transition: background-color 0.2s ease;
}
.color-input-wrapper {
display: flex;
gap: 8px;
margin-bottom: 16px;
}
.color-input-wrapper input {
flex: 1;
padding: 8px 12px;
border: 1px solid #ccc;
border-radius: 6px;
font-family: 'Monaco', 'Consolas', monospace;
font-size: 14px;
}
.copy-button {
padding: 8px 16px;
background: #0066cc;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: 500;
}
.copy-button:hover {
background: #0052a3;
}
Pair the styles with a ColorPicker class for real-time preview updates:
// popup.js - Color Picker Logic
class ColorPicker {
constructor(inputElement, previewElement) {
this.input = inputElement;
this.preview = previewElement;
this.init();
}
init() {
this.input.addEventListener('input', (e) => this.updatePreview(e.target.value));
this.updatePreview(this.input.value);
}
updatePreview(color) {
this.preview.style.backgroundColor = color;
this.input.value = color.toUpperCase();
}
getColor() {
return this.input.value;
}
}
document.addEventListener('DOMContentLoaded', () => {
const picker = new ColorPicker(
document.getElementById('color-input'),
document.getElementById('color-preview')
);
});
Eyedropper API: An Alternative Capture Method
For extensions that need users to sample any pixel on screen—not just computed CSS values—Chrome’s built-in Eyedropper API is a cleaner alternative to injecting content scripts:
async function startEyedropper() {
if (!window.EyeDropper) {
console.warn('EyeDropper API not supported in this browser');
return null;
}
const eyedropper = new EyeDropper();
try {
const result = await eyedropper.open();
return result.sRGBHex; // e.g. "#3b82f6"
} catch (error) {
if (error.name !== 'AbortError') {
console.error('Eyedropper error:', error);
}
return null;
}
}
Use chrome.scripting-based extraction (see above) when you want to automatically harvest all dominant colors from a page without user interaction. Use the Eyedropper API when you want the user to deliberately sample a single pixel from anywhere on screen. Both approaches pair well with the ColorAnalyzer class.
Content Script Integration
For extensions that pick colors directly from web pages, a content script enables click-to-capture:
// content-script.js - Page Color Picker
document.addEventListener('click', (e) => {
if (e.target.dataset.colorPickerActive === 'true') {
const computedStyle = window.getComputedStyle(e.target);
const bgColor = computedStyle.backgroundColor;
chrome.runtime.sendMessage({
type: 'COLOR_PICKED',
color: bgColor
});
}
});
This pattern enables users to click elements on any webpage and capture their colors directly into the extension’s color picker interface.
Accessibility Considerations
Accessible color picker design ensures usability across different abilities. Implement keyboard navigation throughout the picker interface, provide sufficient color contrast in your own UI, and offer non-visual color information through ARIA labels:
<input
type="color"
id="color-input"
aria-label="Select color"
aria-describedby="color-description"
>
<span id="color-description" class="sr-only">
Current color value in hex format
</span>
Testing and Deployment
Load your extension in Chrome through chrome://extensions/ with Developer mode enabled. Test the color picker across different websites, including:
- Single-page applications with dynamic color schemes
- Sites using CSS custom properties for theming
- Pages with dark mode and light mode variants
- Images with various color profiles
Use Chrome’s devtools to debug content scripts and monitor API calls. Check the background service worker console for errors in AI processing.
Conclusion
Building an AI color picker Chrome extension combines traditional color extraction with intelligent analysis. The most useful features for developers and power users include accessibility contrast checking, palette generation, and export in multiple formats. Start with solid color extraction, add accessibility validation, then layer on AI-powered suggestions.
Focus on performance—color analysis should feel instant. Cache results when possible and use Web Workers for heavy computation. Lazy-load color conversion utilities, use event delegation instead of individual listeners, and cache computed colors to avoid redundant calculations. The popup interface loads synchronously, so minimizing initial JavaScript payload improves perceived performance. Users will appreciate having instant access to color information without waiting for network requests.
The extension ecosystem rewards focused tools that solve specific problems well. A color picker that excels at accessibility checking and palette generation will find a dedicated audience among developers who care about design consistency and WCAG compliance.
Related Reading
- Claude Code for Beginners: Complete Getting Started Guide
- Best Claude Skills for Developers in 2026
- Claude Skills Guides Hub
Built by theluckystrike — More at zovo.one