get-image-colors and node-vibrant are both npm packages designed to extract meaningful color palettes from images, commonly used for UI theming, dynamic background generation, or visual analysis. get-image-colors provides a lightweight, promise-based API that returns an array of dominant colors using k-means clustering under the hood. node-vibrant, a Node.js port of Google's Android Palette library, offers a richer set of predefined swatches (Vibrant, Muted, Dark, Light variants) and supports multiple image input types including buffers, URLs, and file paths.
Both get-image-colors and node-vibrant solve the same core problem—analyzing an image to extract representative colors—but they do so with different philosophies, APIs, and output structures. Let’s compare how they handle real-world frontend tasks.
get-image-colors returns a simple array of color objects (typically 5–10 entries), each containing RGB, HSL, and hex values. There’s no built-in meaning—just dominant clusters.
// get-image-colors: raw dominant colors
import getColors from 'get-image-colors';
const colors = await getColors('/path/to/image.jpg');
console.log(colors[0].hex); // e.g., "#ff5733"
console.log(colors[0].rgb); // { r: 255, g: 87, b: 51 }
node-vibrant returns a Palette object with named swatches like Vibrant, Muted, DarkVibrant, etc. Each swatch includes not just the color but also population (pixel count) and body/title text color suggestions.
// node-vibrant: semantic swatches
import Vibrant from 'node-vibrant';
const palette = await Vibrant.from('/path/to/image.jpg').getPalette();
console.log(palette.Vibrant.hex); // e.g., "#d94f3a"
console.log(palette.Muted.rgb); // { r: 120, g: 110, b: 95 }
💡 If your design system needs a “vibrant accent” or “muted background,”
node-vibrantgives you that out of the box. Withget-image-colors, you’d have to implement your own logic to classify colors by saturation or brightness.
get-image-colors accepts a <img> element, image URL, or file path (in Node.js). In the browser, it creates a hidden canvas to read pixel data.
// Browser usage with <img>
const img = document.querySelector('img');
const colors = await getColors(img);
node-vibrant supports image URLs, file paths, and raw buffers in Node.js. In the browser, you must pass a <img> or <canvas> element explicitly.
// Browser usage with <img>
const img = document.querySelector('img');
const palette = await Vibrant.from(img).getPalette();
Both work in browser and Node.js, but neither handles CORS-protected images automatically—you must ensure the image is loaded with proper CORS headers if reading cross-origin pixels.
get-image-colors allows you to specify the number of colors to extract:
const colors = await getColors(image, { count: 3 });
That’s it—no other tuning options.
node-vibrant lets you adjust target color counts per swatch type and set minimum saturation/brightness thresholds:
const palette = await Vibrant.from(image)
.quality(5) // reduce quality for speed
.maxColorCount(64) // limit internal color sampling
.getPalette();
However, you can’t directly control how many total colors are returned—only how the swatches are generated.
Both libraries reject their promises if the image fails to load or is unreadable (e.g., due to CORS or invalid format).
// get-image-colors error handling
try {
const colors = await getColors(badImage);
} catch (err) {
console.error('Color extraction failed:', err.message);
}
// node-vibrant error handling
try {
const palette = await Vibrant.from(badImage).getPalette();
} catch (err) {
console.error('Palette generation failed:', err.message);
}
Note: node-vibrant may return null for individual swatches if no suitable color is found (e.g., palette.DarkVibrant could be null). Always check before accessing properties.
get-image-colors depends on get-pixels and kmeans-js. It loads the entire image into memory and runs k-means clustering, which can be slow on large images.
node-vibrant uses its own quantization and scoring logic based on Android’s Palette algorithm. It downsamples images internally for performance but still processes all pixels unless you lower quality.
Neither is optimized for real-time video frame analysis—both are best suited for static images processed once.
You’re building a music app that shows album art and wants to style the play button with a vibrant accent.
node-vibrantpalette.Vibrant.rgb without guessing which color is most saturated.const palette = await Vibrant.from(albumArt).getPalette();
if (palette.Vibrant) {
playButton.style.backgroundColor = palette.Vibrant.hex;
playButton.style.color = palette.Vibrant.titleTextColor;
}
You want to generate a subtle gradient from the two most dominant colors in a user-uploaded photo.
get-image-colorsconst [c1, c2] = await getColors(photo, { count: 2 });
container.style.background = `linear-gradient(${c1.hex}, ${c2.hex})`;
| Feature | get-image-colors | node-vibrant |
|---|---|---|
| Output | Array of raw color objects | Object with named swatches (Vibrant, etc.) |
| Semantic Labels | ❌ No | ✅ Yes |
| Custom Color Count | ✅ Yes (count option) | ❌ No (fixed swatch set) |
| Text Color Helpers | ❌ No | ✅ Yes (titleTextColor, bodyTextColor) |
| Browser Support | ✅ Via <img> or URL | ✅ Via <img> or <canvas> |
| Node.js Support | ✅ Via file path or buffer | ✅ Via file path, URL, or buffer |
get-image-colors for lightweight, performance-sensitive scenarios where you only need dominant colors and don’t care about their perceptual role.node-vibrant when your UI relies on consistent, human-meaningful color roles (e.g., “use the vibrant color for buttons, muted for backgrounds”) and you can accept slightly heavier processing.Both are actively maintained and production-ready—your choice should hinge on whether you need Google’s Palette semantics or just raw color data.
Choose node-vibrant when you require Google’s Palette-inspired swatch system with clearly defined roles (e.g., Vibrant, Muted, DarkVibrant). It’s better suited for applications that dynamically theme UI elements based on image content—like media players or photo galleries—where consistent, human-readable color roles are essential. Be aware that it has more runtime overhead due to its comprehensive palette generation logic.
Choose get-image-colors if you need a minimal, fast, and straightforward way to extract a small set of dominant colors without complex configuration. It’s ideal for simple use cases like generating a background tint or picking a primary accent color, especially in environments where bundle size or dependency count matters. However, it doesn’t provide semantic labels like 'vibrant' or 'muted', so you’ll need to post-process results if you need those distinctions.
Extract prominent colors from an image.