Уявімо ситуацію: ми додаємо зображення до матеріалу на сайті й хочемо, щоб дизайн сторінки підлаштувався під його настрій. Наприклад, фон або акцентні кольори — щоб усе виглядало гармонійно. Для цього можна витягти основний колір із зображення і створити на його основі палітру — світліші й темніші відтінки.

У цій статті покажу, як це зробити на чистому PHP, без жодних зовнішніх бібліотек. Просто — і працює практично всюди.

Початковий код

Щоб краще побачити результат, візьмемо папку з кількома зображеннями і пройдемося по ній циклом. Для кожного зображення отримаємо ключовий колір і згенеруємо палітру кольорів. Це — лише для демонстрації.

У реальному сценарії, наприклад у шаблоні статті чи галереї, зазвичай обробляється лише одне головне зображення. Але для тесту — більше, щоб оцінити, як працює алгоритм на різних картинках.
$img_dir = __DIR__ . '/images'; // впиши тут шлях до своєї папки з зображеннями
$img_url_base = SITE_URL . '/my-awesome-slug/images'; // а тут має бути URL, по якому папка з зображеннями видима в браузері

$files = glob($img_dir . '/*.jpg'); // або .webp/.png

foreach ($files as $img_path) {
	$filename = basename($img_path);
	$img_url = $img_url_base . '/' . $filename;

	$key_color = get_dominant_color($img_path); // витягуємо ключовий колір з зображення
	draw_image_block($img_url, $key_color); // "малюємо" блок із зображенням на фоні ключового кольору
	echo '<br>'; // перенесення рядка для краси 😉

	// генеруємо палітру із ключового кольору і відразу виводимо її під зображенням
	$palette = generate_color_palette($key_color);
	echo '<div style="width: 100%; display: flex; gap: 20px; justify-content: center; align-items: center;">';
	foreach ($palette as $color) {
		echo '<div style="background-color:' . $color . '; width:200px;height:80px;display:flex;justify-content: center;align-items: center; color: #fff;">' . $color . '</div>';
	}
	echo '</div><br>';
}

Як визначити "ключовий" колір?

Ключовим ми називаємо приблизно середній колір зображення. Не найпоширеніший (це складніше), а саме усереднений. Ідея проста:

  • зменшуємо зображення до мініатюри (щоб обробка була швидкою);

  • проходимо по кожному пікселю і обчислюємо середнє значення для R, G і B;

  • результат — колір у форматі HEX (#rrggbb), який візуально відповідає "настрою" картинки.

function get_dominant_color($image_path, $sample_size = 100)
{
	if (!file_exists($image_path)) return '#cccccc';

	$image_info = getimagesize($image_path);
	$mime = $image_info['mime'];

	// визначаємо тип картинки (в даному випадку нам це не треба, оскільки ми відразу вказали розширення jpg для файлів, але хай буде – для краси знову ж таки 😂
	switch ($mime) {
		case 'image/jpeg':
			$image = imagecreatefromjpeg($image_path);
			break;
		case 'image/png':
			$image = imagecreatefrompng($image_path);
			break;
		case 'image/webp':
			if (!function_exists('imagecreatefromwebp')) return '#cccccc';
			$image = imagecreatefromwebp($image_path);
			break;
		default:
			return '#cccccc';
	}

	if (!$image) return '#cccccc';

	$width = imagesx($image);
	$height = imagesy($image);

	// Зменшуємо зображення до приблизно $sample_size пікселів у загальному
	$scale = sqrt($sample_size / ($width * $height));
	$new_w = max(1, intval($width * $scale));
	$new_h = max(1, intval($height * $scale));

	$resized = imagecreatetruecolor($new_w, $new_h);
	imagecopyresampled($resized, $image, 0, 0, 0, 0, $new_w, $new_h, $width, $height);

	// Підрахунок середнього кольору
	$total_r = $total_g = $total_b = 0;
	$count = 0;

	for ($x = 0; $x < $new_w; $x++) {
		for ($y = 0; $y < $new_h; $y++) {
			$rgb = imagecolorat($resized, $x, $y);
			$r = ($rgb >> 16) & 0xFF;
			$g = ($rgb >> 8) & 0xFF;
			$b = $rgb & 0xFF;

			$total_r += $r;
			$total_g += $g;
			$total_b += $b;
			$count++;
		}
	}

	imagedestroy($image);
	imagedestroy($resized);

	if ($count === 0) return '#cccccc'; // вже вкотре – у будь-якому незрозумілому випадку повертаємо сірий колір і виходимо з функції 😉

	$avg_r = round($total_r / $count);
	$avg_g = round($total_g / $count);
	$avg_b = round($total_b / $count);

	return sprintf("#%02x%02x%02x", $avg_r, $avg_g, $avg_b); // перед тим, як повернути колір – конвертуємо його в HEX-формат
}

Створюємо палітру: темніші і світліші варіанти

Маючи ключовий колір, ми хочемо створити палітру з кількох відтінків: і світліших, і темніших. Для цього:

  1. Перетворюємо HEX у HSL (відтінок, насиченість, яскравість).

  2. Трохи змінюємо яскравість (lightness), зберігаючи відтінок і насиченість.

  3. Повертаємо результат у формат HEX.

function generate_color_palette(string $hex, int $steps = 3): array {
	// Перетворення HEX → RGB
	$hex = ltrim($hex, '#');
	$r = hexdec(substr($hex, 0, 2)) / 255;
	$g = hexdec(substr($hex, 2, 2)) / 255;
	$b = hexdec(substr($hex, 4, 2)) / 255;

	// RGB → HSL
	$max = max($r, $g, $b);
	$min = min($r, $g, $b);
	$l = ($max + $min) / 2;

	if ($max === $min) {
		$h = $s = 0;
	} else {
		$d = $max - $min;
		$s = $l > 0.5 ? $d / (2.0 - $max - $min) : $d / ($max + $min);

		switch ($max) {
			case $r: $h = ($g - $b) / $d + ($g < $b ? 6 : 0); break;
			case $g: $h = ($b - $r) / $d + 2; break;
			case $b: $h = ($r - $g) / $d + 4; break;
		}
		$h /= 6;
	}

	// Параметри
	$h = round($h * 360); // В градусах
	$s = round($s * 100); // %
	$l = round($l * 100); // %

	// Визначаємо діапазон яскравостей
	$min_l = 15;  // мінімальний L, щоб не було чорного
	$max_l = 95;  // максимальний L, щоб не було білого

	// Генеруємо палітру
	$palette = [];

	for ($i = -$steps; $i <= $steps; $i++) {
		$offset = $i / $steps;
		$new_l = $l + $offset * ($offset < 0 ? ($l - $min_l) : ($max_l - $l));
		$new_l = max($min_l, min($max_l, round($new_l)));

		$palette[] = hsl_to_hex($h, $s, $new_l);
	}

	return $palette;
}

// Допоміжна функція для конвертації кольору з HSL в HEX
function hsl_to_hex($h, $s, $l): string {
	$h /= 360;
	$s /= 100;
	$l /= 100;

	$a = $s * min($l, 1 - $l);

	$convert = function ($n) use ($h, $a, $l) {
		$k = fmod($n + $h * 12, 12);
		$c = $l - $a * max(min(min($k - 3, 9 - $k), 1), -1);
		return round($c * 255);
	};

	return sprintf("#%02x%02x%02x", $convert(0), $convert(8), $convert(4));
}

Виводимо результат красиво

Щоб краще оцінити палітру, обгорнемо зображення у блок з фоном відповідного кольору. Для візуальної естетики додамо маску:

function draw_image_block($img_url, $key_color) {
	echo '<div style="background-color:' . $key_color . '; 100%; text-align: center;">';
	echo '<img src="' . $img_url . '" style="width:60%; max-width:900px; height:auto; mask-image: linear-gradient(to right, transparent 0%, black 10%, black 90%, transparent 100%); mask-repeat: no-repeat;" />';
	echo '</div>';
}

Підсумок

  • Ми навчилися отримувати ключовий колір із будь-якого зображення на PHP, без сторонніх залежностей. Це забезпечує просто блискавичну швидкість отримання ключового кольору і палітри для нього.

  • Усереднений колір достатньо добре передає загальний настрій картинки.

  • Побудова палітри на основі HSL дозволяє зберегти відтінок, змінюючи лише світлість — що дає приємні варіанти для дизайну.

Можеш використовувати це для створення динамічних тем, адаптивних заголовків, фонових блоків або просто щоб зробити сторінку більш живою.

Результат виконання коду (і швидкість його роботи) можна подивитися на оцій сторінці – приклад.