Artist studio desk with color swatches and a palette displayed on a monitor

Swatchify: Pull Colors Straight Out of Any Image

The Gray Cat
The Gray Cat

Every designer, every front-end developer, every product team that has ever wanted to auto-theme a page based on an uploaded photo has asked the same question: what are the main colors in this image? Swatchify answers that question entirely in the browser, with zero runtime dependencies, a bundle size around 2KB gzipped, and a k-means++ clustering algorithm that returns not just color values but how much of the image each color occupies. Whether you are building an image upload preview, a brand color analyzer, or a dynamic theming engine, Swatchify hands you the data you need in a single function call.

A Palette in Your Pocket

Swatchify ships four functions. The star of the show is extractColors, which accepts nearly any image source you can throw at it: an HTMLImageElement, an HTMLCanvasElement, raw ImageData, a File, a Blob, or a plain URL string. It returns a promise that resolves to an array of extracted colors, each with a hex string, an RGB object, and a percentage indicating how dominant that color is. The remaining three helpers handle palette visualization and contrast calculation.

Because Swatchify relies on the Canvas API, it runs exclusively in the browser. There is no Node.js support, which keeps the library focused and small. If you need server-side extraction, the companion Go CLI covers that territory, but for client-side work the JavaScript library is all you need.

Getting Started

The npm package is published under the name swatchme. Install it with your package manager of choice:

npm install swatchme
yarn add swatchme

Then import the functions you need:

import { extractColors, generatePaletteCanvas } from "swatchme";

Pulling Colors from a Photo

From a File Input

The most common scenario is extracting colors from a user-uploaded image. Wire up a file input and pass the selected file directly:

import { extractColors } from "swatchme";

const input = document.querySelector<HTMLInputElement>('input[type="file"]');

input?.addEventListener("change", async () => {
  const file = input.files?.[0];
  if (!file) return;

  const colors = await extractColors(file);

  colors.forEach((color) => {
    console.log(`${color.hex}${color.percentage.toFixed(1)}%`);
  });
});

Each entry in the returned array looks like this:

interface ExtractedColor {
  hex: string;
  percentage: number;
  rgb: { r: number; g: number; b: number };
}

The percentage field is what sets Swatchify apart from many alternatives. Knowing that a particular blue occupies 42% of the image versus 3% lets you make smarter decisions about which color to use as a background, which to use as an accent, and which to ignore entirely.

From an Image Element

If the image is already on the page, pass the element directly:

const img = document.querySelector<HTMLImageElement>("#hero-photo");

if (img) {
  const colors = await extractColors(img, {
    numColors: 3,
    excludeWhite: true,
  });

  document.body.style.backgroundColor = colors[0].hex;
}

From a URL

You can also pass a URL string. Swatchify will load the image internally before extracting:

const colors = await extractColors("https://example.com/photo.jpg", {
  quality: 80,
});

Tuning the Extraction

The Quality Dial

The quality option accepts a number from 0 to 100. Lower values downsample the image more aggressively before running the clustering algorithm, which means faster results at the cost of some accuracy. Higher values preserve more detail. The default of 50 is a solid middle ground. For a quick preview thumbnail you might drop to 20; for a design tool that needs precision, push it to 90 or higher.

const fast = await extractColors(file, { quality: 10 });
const precise = await extractColors(file, { quality: 95 });

Internally, quality maps to a maximum pixel dimension between 100 and 500 pixels. The image is downscaled to that dimension before pixel data is read, so even a 4000px photograph processes in milliseconds at lower quality settings.

Filtering Out Noise

Photographs with white backgrounds or dark borders can skew results. Two boolean options handle the most common cases:

const colors = await extractColors(file, {
  excludeWhite: true,
  excludeBlack: true,
  colorThreshold: 40,
});

The colorThreshold option controls how generous the white and black detection is. A higher value catches more near-white and near-black pixels. The default of 30 works well for product photography; bump it up for images with soft gradients near the edges.

Enforcing Color Diversity

If you want the returned palette to contain visibly distinct colors, set minContrast to a positive number. This enforces a minimum distance between extracted colors in RGB space, preventing the algorithm from returning five slightly different shades of the same blue:

const colors = await extractColors(file, {
  numColors: 6,
  minContrast: 50,
});

Painting the Palette

Canvas Visualization

Swatchify includes a built-in palette renderer. generatePaletteCanvas takes the array of extracted colors and returns an HTMLCanvasElement with proportional color blocks and hex labels:

import { extractColors, generatePaletteCanvas } from "swatchme";

const colors = await extractColors(file);
const palette = generatePaletteCanvas(colors, 600, 80);

document.getElementById("palette-container")?.appendChild(palette);

Each block is sized according to the color's percentage, so the dominant color gets the widest stripe. The hex code is rendered on top of each block using a contrasting text color calculated by the built-in getContrastColor utility.

Data URL Export

If you need the palette as an image rather than a DOM element, generatePaletteDataURL returns a PNG data URL:

import { generatePaletteDataURL } from "swatchme";

const dataUrl = generatePaletteDataURL(colors, 600, 80);
const img = new Image();
img.src = dataUrl;

This is handy for download buttons, clipboard operations, or embedding palettes in generated reports.

Contrast Helper

The getContrastColor utility returns either black or white depending on which provides better readability against a given background:

import { getContrastColor } from "swatchme";

colors.forEach((color) => {
  const textColor = getContrastColor(color.rgb);
  const swatch = document.createElement("div");
  swatch.style.backgroundColor = color.hex;
  swatch.style.color = textColor;
  swatch.textContent = color.hex;
  container.appendChild(swatch);
});

Building an Auto-Theme Component

Combining everything, here is a small utility that takes a file and returns a theme object ready for CSS custom properties:

import { extractColors, getContrastColor } from "swatchme";

interface Theme {
  primary: string;
  secondary: string;
  accent: string;
  textOnPrimary: string;
}

async function buildTheme(file: File): Promise<Theme> {
  const colors = await extractColors(file, {
    numColors: 5,
    excludeWhite: true,
    excludeBlack: true,
    quality: 70,
    minContrast: 40,
  });

  const [primary, secondary, accent] = colors;

  return {
    primary: primary.hex,
    secondary: secondary?.hex ?? primary.hex,
    accent: accent?.hex ?? secondary?.hex ?? primary.hex,
    textOnPrimary: getContrastColor(primary.rgb),
  };
}

const theme = await buildTheme(uploadedFile);
document.documentElement.style.setProperty("--color-primary", theme.primary);
document.documentElement.style.setProperty("--text-on-primary", theme.textOnPrimary);

This pattern works well for image upload flows where the page adapts its color scheme to match the content the user just provided.

Wrapping Up

Swatchify earns its place in the toolbox by doing one thing well: extracting dominant colors from images entirely in the browser with no dependencies and almost no bundle cost. The percentage data, smart filtering options, and built-in palette visualization push it beyond a simple color picker into genuinely useful territory for design tools, theming systems, and creative applications. At version 0.1.0 the library is young, but the API surface is clean and the k-means++ algorithm is a proven approach. If your next project involves images and colors, Swatchify is worth a look.