Library / SDK
ksubileau/color-thief-php avatar
ksubileau/color-thief-php

color-thief-php extracts dominant colors in PHP, and v3 removed the one convenience that made it dangerous

Grabs the dominant color or a representative color palette from an image. Uses PHP and GD, Imagick or Gmagick.

638 stars68 forksPHPMIT

At a glance

What is it?
ksubileau/color-thief-php is a PHP port of the Color Thief JavaScript library, built on MMCQ quantization and backed by GD, Imagick or Gmagick, returning hex, CSS rgb, HSL, OKLCH, CMYK, semantic swatches and WCAG accessibility helpers. Version 3 dropped loading images from URLs, which removes the SSRF footgun and moves fetching to the caller.
Who is it for?
Use color-thief-php when a PHP application needs a dominant color or a ranked palette from an image it already holds, for example to theme a user avatar or a product card, and you have GD, Imagick or Gmagick available. Do not adopt it if you need perceptual colour matching or colour difference scoring, because the documented API stops at extraction and conversion with no comparison method.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 120 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Extraction with MMCQ, the algorithm behind Color Thief

The library grabs the dominant colour or a representative palette from an image, and the interesting part is where the algorithm comes from.

Color Thief PHP is a PHP port of the Color Thief JavaScript library, and it uses the MMCQ algorithm, modified median cut quantization, from the Leptonica library. Modified median cut is the standard way to reduce a large pixel population to a small set of representative buckets: the colour space is divided along its longest axes, each bucket is split, and the buckets are weighted by the number of pixels they hold. That weighting is why a dominant colour survives the reduction while a small but saturated accent can still appear in the palette.

The design point is that the algorithm is not reimplemented from scratch, so the PHP port inherits the behaviour of a well known implementation rather than inventing a colour reduction scheme of its own.

The output is a set of colour objects rather than raw values, and that is where this port goes beyond the original scope. Results come back in hex, CSS rgb, HSL, OKLCH, CMYK, or as a packed integer. A single dominant colour, a ranked palette of up to 20 colours, or semantic swatches can be requested. The documented use cases are generating dynamic themes from a user avatar, building coherent product cards from cover art, creating adaptive backgrounds for media players, and analysing the dominant hues across a set of images.

Composer install and the first call to getColor()

The recommended install is a single Composer command:

bash
composer require ksubileau/color-thief-php

Everything is exposed through the ColorThief\ColorThief class, and the documentation advises instantiating it once and reusing it across images, which is the usual pattern for a wrapper around an image extension that may hold resources.

php
use ColorThief\ColorThief;
 
$thief = new ColorThief();
$sourceImage = '/path/to/image.jpg';
$color = $thief->getColor($sourceImage);
 
echo $color->toCss();    // "rgb(98, 42, 131)"
echo $color->toHex('#'); // "#622a83"

The method returns an RgbColor object, or null if no colour could be extracted. That null case matters in practice: an image that is entirely transparent, or one the extension fails to decode, will not produce an exception here but an empty result, so a caller that assumes a colour exists has to handle it.

The runtime requirements are stated plainly: PHP 8.2 or higher, the Fileinfo extension, and at least one image processing extension. GD 2.0 or newer, Imagick 2.0 or newer (3.0 or newer for CMYK images), or Gmagick 1.0 or newer. JPEG, PNG, GIF and WEBP are supported.

The Imagick CMYK caveat is the sort of thing that bites later: a CMYK source image handled by Imagick below version 3.0 will not give the colour results the library documents.

Palettes are ranked, swatches are classified

The palette and the swatch API answer different questions, and keeping them apart is the design that makes the library more useful than a simple average.

A palette is a ranked list. getPalette() returns a ColorPalette object that is countable, iterable and array accessible, with the colours ordered from most to least dominant:

php
$palette = $thief->getPalette($sourceImage);
 
foreach ($palette as $color) {
    echo $color->toHex('#') . "\n";
}
// #622a83
// #d4a832
// #1e6b4a

A swatch set is a classification. getSwatches() takes the palette colours and assigns them to up to six named roles: vibrant, muted, darkVibrant, darkMuted, lightVibrant and lightMuted. Each property holds an RgbColor or null when no palette colour matched that role.

php
$swatches = $thief->getSwatches($sourceImage);
 
echo $swatches->vibrant?->toHex('#');      // e.g. "#e03c7d"
echo $swatches->darkVibrant?->toHex('#');  // e.g. "#7b1c2e"
echo $swatches->muted?->toHex('#');        // e.g. "#b07a92"

The null safe operator on those properties is not decoration. A photo with no clearly muted tone will simply have no muted swatch, and code that assumes all six roles exist will read nulls.

The palette size is a parameter, passed by name in the documented example as colorCount: 6, and the documentation states that a palette of up to 20 colours can be requested.

Five input types, and the URL support that v3 removed

The three main methods accept the same set of source types for the source image parameter: a file path string, a GD resource which is a \GdImage, an Imagick instance, a Gmagick instance, or raw image binary content as a string.

That flexibility is useful when the image arrives by different routes: a file on disk, bytes from an upload, or a resource produced by another image operation.

The file path case carries a warning in the documentation: you must ensure that the path is sanitised before calling Color Thief. That is a real requirement rather than a formality, since an unsanitised path lets a caller read arbitrary files on the server.

The bigger change in version 3 is the removal of URL loading. The documentation states plainly that loading images from URLs is no longer supported in v3, and that if you need to process a remote image you should fetch it yourself before passing the content to Color Thief, with the v2 to v3 migration guide referenced.

That change removes a genuinely dangerous default. A library that fetches a URL on your behalf turns every image call into a potential server side request forgery vector, and it also means your server makes outbound requests to an address the caller chose. Moving the fetch to the caller means the timeout, the size limit, the allow list and the redirect policy all live in code you control and can audit.

The cost is one line of extra code in your application, and one more thing to get right.

Accessibility helpers and population data on every colour

The colour objects carry more than conversion methods, and the extra methods are where this port is more capable than a bare colour extractor.

Conversion targets from any colour object are toRgb(), toOklch(), toHsl(), toHsv() and toCmyk(). Each is chainable, and the CSS and array forms are documented side by side:

php
$color->toCss();            // "rgb(98, 42, 131)"
$color->toOklch()->toCss(); // "oklch(0.3721 0.1584 307.45)"
$color->toHsl()->toCss();   // "hsl(277, 52%, 34%)"
$color->toHex();     // "622a83"
$color->toHex('#');  // "#622a83"
$color->toArray();            // [98, 42, 131]
$color->toInt(); // 6432387

OKLCH support is worth calling out for anyone building modern interfaces, since perceptual lightness makes it easier to pick a matching foreground colour than HSL does.

The accessibility helpers are the other differentiator. The colour object exposes luminance(), which returns the WCAG 2.x relative luminance, isDark(), which returns a boolean, and textColor(), which returns an RgbColor such as white text on a dark background. That last one turns the common light or dark text decision into a one line call instead of a threshold you tune yourself.

Two properties describe how much the colour actually matters in the source image: population() returns the number of pixels the colour represents, and proportion() returns the fraction of analysed pixels between 0 and 1. In the documented example, a colour with a population of 4821 has a proportion of 0.127, so it covers roughly an eighth of the analysed pixels. Those two numbers let a caller reject an accent colour that appears in a single pixel and keep only what dominates.

Version 3, a PHP 8.2 floor, and MIT terms

The release history is short and recent at the top. The published versions are v3.0.0 from 2026-06-26, v2.0.2 from 2025-07-17 and v2.0.1 from 2022-11-12. The repository is not archived and the last push was on 2026-06-26, the same day as the v3.0.0 tag.

That is a well maintained shape for a library of this size: a major version a few months old, a minor release the year before, and an active commit history.

The v3 break is worth understanding before you upgrade, because it is not only the URL removal. The PHP floor moved to 8.2, which rules out a lot of shared hosting still on 8.0 or 8.1. The Imagick version note changed as well, with 3.0 or newer required for CMYK images.

The licence is MIT, which is about as permissive as a licence gets and imposes no obligations on the applications that use the library.

The repository layout shows the tooling behind it: composer.json for the package definition, phpstan.neon.dist for static analysis, rector.php for automated refactoring, .php-cs-fixer.dist.php for code style, and a tests/ directory that a GitHub Actions workflow runs. The CHANGELOG.md is present for tracking the v2 to v3 differences.

Editorial conclusion

Use color-thief-php when a PHP application needs a dominant color or a ranked palette from an image it already holds, for example to theme a user avatar or a product card, and you have GD, Imagick or Gmagick available. Do not adopt it if you need perceptual colour matching or colour difference scoring, because the documented API stops at extraction and conversion with no comparison method. Before integrating, confirm your PHP runtime is 8.2 or newer, check that your image extension supports the formats you accept, and fetch any remote image yourself before passing its bytes in, since v3 no longer accepts URLs.

Frequently asked questions

What is color-thief-php used for?

It is a PHP library for grabbing the dominant color or a representative color palette from an image, without manual color picking. It is a port of the Color Thief JavaScript library and uses the MMCQ algorithm from the Leptonica library.

How do I install color-thief-php?

Through Composer with composer require ksubileau/color-thief-php. The library requires PHP 8.2 or higher, the Fileinfo extension, and one of GD 2.0+, Imagick 2.0+ or Gmagick 1.0+.

Can color-thief-php load an image from a URL?

No, not since version 3. The documentation states that URL loading is no longer supported and that remote images should be fetched by the caller before the content is passed in.

What color formats does color-thief-php return?

Any color object can be converted with toRgb(), toOklch(), toHsl(), toHsv() and toCmyk(), and rendered as CSS, hexadecimal, a component array or a packed integer. Palettes expose the same conversions applied to every color at once.

Does color-thief-php help with accessibility?

Yes. Each color exposes luminance() for the WCAG 2.x relative luminance, isDark() as a boolean, and textColor(), which returns a contrasting RgbColor to place on that background.

What are the semantic swatches in color-thief-php?

getSwatches() classifies the palette into up to six named roles: vibrant, muted, darkVibrant, darkMuted, lightVibrant and lightMuted. Each property holds an RgbColor or null when no palette color matched that role.

Official sources

  1. Issues
  2. ksubileau/color-thief-php on GitHub
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/ksubileau-color-thief-php.svg)](https://hysenlabs.com/projects/ksubileau-color-thief-php)

Was this useful?