Inspired by this great blog post by Manuel Wieser, I made a php script to find the dominant color in an image, and create a data-url containing a colored "spacer" gif.

The hardest part was to figure out how the node.js "buffer" functions compare to php's "pack" function. For all the other cryptical stuff about what's needed to create a small gif on the fly, see the post linked above.
For my demo I wanted not only the blank gif, but also the background to appear in the image's dominant color.
Because darker background colors would clash with the text color, I also needed a way to check the contrast of the background- and text-color -- this blog post about color contrast was a huge help. I slightly changed the "lumdiff" function and use it to decide if black or white text color is used in the demo. The color choices for text, borders and the button are hard coded at the moment and the dynamic values are injected in an inline style.

Direct link to the demo

This is the slightly modified function for the color contrast. In the original function six arguments were needed, I changed that to two arguments, one array each for the two colors that will be compared.

function lumdiff($col1,$col2){
	if(!isset($col1) || !isset($col2)){
		return false;
	} else {
		$R1 = $col1[0];
		$G1 = $col1[1];
		$B1 = $col1[2];
 
		$R2 = $col2[0];
		$G2 = $col2[1];
		$B2 = $col2[2];
 
		$L1 = 0.2126 * pow($R1/255, 2.2) +
			  0.7152 * pow($G1/255, 2.2) +
			  0.0722 * pow($B1/255, 2.2);
 
		$L2 = 0.2126 * pow($R2/255, 2.2) +
			  0.7152 * pow($G2/255, 2.2) +
			  0.0722 * pow($B2/255, 2.2);
 
		if($L1 > $L2){
			return ($L1+0.05) / ($L2+0.05);
		}else{
			return ($L2+0.05) / ($L1+0.05);
		}
	}
}

This is my function to return the data-url string for the placeholder gif. The only argument is the color, which is expected as a hex value like "990000". Currently there is no check or sanitization of that argument, which is not good and needs to be changed, asap - fixed. :-)

function getColoredPlaceholder($imgColor){
	if(!isset($imgColor) || !isHex($imgColor) ){
		return false;
	}
	$header                  = pack('H*',"474946383961");
	$logicalScreenDescriptor = pack('H*',"01000100800100");
	$imageColor              = pack('H*',$imgColor);
	$colorPad                = pack('H*',"000000");
	$imageDescriptor         = pack('H*',"2c000000000100010000");
	$imageData               = pack('H*',"0202440100");
 
	$binary  = $header . $logicalScreenDescriptor . $imageColor . $colorPad . $imageDescriptor . $imageData;
	$dataUrl = 'data:image/gif;base64,'.base64_encode($binary);
 
	return $dataUrl;
}

This is the code to get the dominant color. It needs Imagick to be installed on the server, which was the case in my environment, so I didn't check if or how this would work with GDlib.

// $img = local or remote url to an image file
$image = new Imagick($img);
$w = $image->width;
$h = $image->height;
$aspectRatio = ($h/$w);
$image->resizeImage(150, 150, Imagick::FILTER_GAUSSIAN, 1);
$image->quantizeImage(1, Imagick::COLORSPACE_LAB, 0, false, false);
$image->setFormat('RGB');
 
$hexImg = bin2hex($image);
$imgColor = substr($hexImg, 0, 6);  // ee ff cc
$r = hexdec(substr($hexImg, 0, 2)); // ee -> 238
$g = hexdec(substr($hexImg, 2, 2)); // ff -> 255
$b = hexdec(substr($hexImg, 4, 2)); // cc -> 204
 
// values for ui colors contrasting with the image color
$textColor   = '#FFF';
$borderColor = '#CCC';
$inversColor = '#333';
$contrast = lumdiff(array($r,$g,$b),array(0,0,0));
if($contrast >= 5){
	$textColor   = '#000';
	$borderColor = '#333';
	$inversColor = '#CCC';
}
// the placeholder image string
$dataUrl = getColoredPlaceholder($imgColor);

And here's part of the output section (the complete markup can be viewed in the source of the demo above or on github)

echo '
<figure class="item-wrap">
<div class="image-wrap" style="padding-top: ' . 100*$aspectRatio . '%;" data-ar="' . $aspectRatio . '">
<img class="item-image lazyload" width="' . $w . '" height="' . $h . '" src="' . $dataUrl . '" data-src="' . $img . '"/>
</div>
<figcaption class="item-info">
 This image\'s dominant color is: <strong>#' . $imgColor . '</strong>
</figcaption>
</figure>';

Next I'll try to make something useful with this, for the moment I am happy that I was able to make it work :-)