Warning
This library is still in development and the API is subject to change.
- The entry point is
extractColors.ts - use
pnpm testto launch the tests - use
pnpm serveto get a visual preview of what is happening onlocalhost:3000
With an emphasis on
- Accuracy, using a clustering algorithm and saliency feature detection to find the most representative colors. Other libraries use a simple histogram and manual thresholding / quantization
- Usability, by returning colors that are visually distinct and can be used in a design. Other libraries return "just the main colors", we also look for contrast and accents
- Speed, by using worker threads and array buffers. Other libraries are synchronous and require a lot of memory to run
const colors = await extractColors(buffer, 3, {
workers: true,
colorSpace: oklabSpace,
strategy: gapStatisticKmeans({ max: 10 }),
clamp: 0.005,
})| image | step |
|---|---|
![]() |
original image (here the original image is scrambled to avoid infringing on copyrights) |
![]() |
main colors are extracted by k-means clustering |
![]() |
the background is picked from extracted colors by looking at the most prevalent colors outside of the masked center |
![]() |
the foreground is picked from the most salient features by Itti-Koch filtering of the image |
![]() |
main colors are split into 2 pools depending on their proximity with either the background or the foreground |
![]() |
|
| finally we look back at the original image to determine if the background and the alternate background formed a gradient in the original image. This allows us to use either flat color or a gradient in our UI. |
A lot of the work done by this library is CPU intensive, so if performance is at all a concern, you should enable multithreading by using the workers: true option. If piscina is installed, it will be used to manage the worker pool, and the pool can be provided (worker: pool) to integrate with the rest of your application
Because changes on "color manipulation" algorithms can be chaotic, we need to be able to test our resulting colors with some form of fuzzy matching. Additionally, color codes don't immediately mentally map to actual colors, making tests hard to read.
For this we base our tests on "named colors" (taken from the CSS list):
t.diagnostic(`accent: ${hex(accent)} >> ${nameColor(accent)} (${simpleColor(accent)})`)
assert.strictEqual(nameColor(accent), 'lightcoral')
// will output: ℹ accent: #f85963 >> lightcoral (salmon)




