Responsive Images Using Picturefill and PHP

Share this article

One of the key challenges with responsive web design, and a subject of much discussion in recent years, is how to deal with images. Setting a max-width on image elements enables designers to allow their size to adapt to the page dimensions, but in itself that approach can lead to far bigger images being downloaded than are required.

One solution to this is “source sets” – referencing separate image files at varying sizes (and by extension, file sizes) to be requested and displayed at different resolutions. Indeed, the srcset attribute is being implemented by Webkit. You can use a similar approach straight away and in a cross-browser compatible manner by using Javascript; one such method is the Picturefill plugin.

In essence, Picturefill allows you to specify different src attributes for an image, each image file corresponding to a different media query. Thus a large image will be fetched if – and only if – the screen size requires it, and likewise a mobile-optimised version of an image will be fetched and displayed as appropriate.

This approach requires some more effort, however – the images themselves need to be created at the appropriate sizes. That’s the focus of this article.

What we’ll Create

I’m going to demonstrate a simple application for generating responsive images, or image derivatives – i.e. different versions / sizes – of images, on demand.

In essence, we’re going to “hijack” requests for a specific version of an image when the requested file doesn’t exist. When it doesn’t we’ll create it, and save it to the file system for subsequent requests.

Getting Started

I’m basing this example on SlimBootstrap, which is a skeleton application which uses the Slim Framework. Alternatively, you can clone or download the complete code for this tutorial from Github.

Once you’ve cloned or downloaded the skeleton application, there are a few more steps before beginning coding.

In this example I’m using ImageMagick, so you’ll need to ensure that it’s installed along with the Imagick PHP extension – refer to the installation instructions for details according to your operating system. If you prefer, you could always rewrite the example using GD or a library such as Imagine.

Then you’ll need to download the Picturefill library. I’ve placed the two relevant files – matchmedia.js and picturefill.js – in public/js/lib.

Create the configuration file config/config.php – there’s a skeleton example in the same folder – and add the following:

'images.dir' => $basedir . 'public/img/', 'logs.dir' => $basedir . 'logs/'

Finally, ensure that the images directory is both writeable by the web server, and it can create sub-directories – chmod 775 should do the trick. It is recommended you do the same for the logs directory – so that if you do run into any errors, they get conveniently printed there.

Using Picturefill

Picturefill takes a “source set” referring to different versions of an image, and selects which one to download and display using media queries. There is a simple demo on Github.

To use it, you need to create a <span> element with the following structure:

<span data-picture data-alt="Image description goes here">
    <span data-src="img/small.jpg"></span>
    <span data-src="img/medium.jpg"     data-media="(min-width: 400px)"></span>
    <span data-src="img/large.jpg"      data-media="(min-width: 800px)"></span>
    <span data-src="img/extralarge.jpg" data-media="(min-width: 1000px)"></span>

    <!-- Fallback content for non-JS browsers. Same img src as the initial, unqualified source element. -->
    <noscript>
        <img src="img/small.jpg" alt="Image description goes here">
    </noscript>
</span>

The data-src attribute contains the URL to each image derivative, and the data-media attribute accepts any valid media query, for example min- or max- width, or min-resolution.

Image Paths and Routing

Here’s an example of the sort of path we’re going to provide:

/img/large/uploads/blog/photo.jpg

Breaking that down;

img is the images directory

large is the derivative; in the following example this could also be small, medium or extralarge

uploads/path is a sub-directory – two-deep, in this example – for organising the images

test.jpg is the filename

If the file referenced by this path exists then the browser will just fetch it, bypassing any routes we have set up. But it’s that route that we’ll need to define, in order to intercept requests for derivatives that don’t yet exist.

We can match all such requests using the following pattern:

$app->get(
    '/img/:parts+',
    function ($parts) use ($app, $c) {

The path above results in the $parts variable being populated as follows:

array(4) {
      [0]=>
          string(5) "large"
      [1]=>
          string(7) "uploads"
      [2]=>
          string(4) "blog"
      [3]=>
          string(9) "photo.jpg"
}

Extracting the relevant parts is easy, by whittling away at this array:

// Grab the filename
$filename = array_pop($parts);

// Grab the derivative
$derivative = array_shift($parts);

// Get the path
$path = implode("/", $parts);

Because we’ve taken the derivative and filename out of the array, it doesn’t matter how many levels deep the file is, as we just implode the remaining array to create the path.

The next step is to check that the destination directory exists – and if not, create it.

// assemble the destination path
$destination_path = $c['config']['images.dir'] .     $derivative . '/' . $path;

// Create the directory, if required
if (!file_exists($destination_path)) {
    mkdir($destination_path, 0777, true);
}

The third argument to mkdir is important; it’s going to be nexcessary to recursively create directories.
Configuring the Derivatives

Let’s create a flexible configuration which for each derivative, defines the name (which will form part of the URL), the appropriate size, and the corresponding media query.

Here’s an example in JSON:

{
    "jpeg_compression": 80,
    "sizes": {
        "small" : {            
            "width" : 180            
        },
        "medium" : {            
            "width" : 375,
            "query" : "(min-width: 400px)"
        },
        "large" : {            
            "width" : 480,
            "query" : "(min-width: 800px)"
        },
        "extralarge" : {            
            "width" : 768,
            "query" : "(min-width: 1000px)"
        }
    }
}

In this example I’ve only defined widths, but the resize function I’m going to use allows you to define either the width or the height – or both. It also provides cropping functions, but for simplicity I’ll just use scaleImage – see the documentation for details.

Next Steps

The next step is to locate the file to process, load the configuration and then resize the image.

// Now get the source path
$source_path = $c['config']['images.dir'] . $path;

// grab the config
$config = json_decode(file_get_contents('../config/images.json'), true);

// get the specs from the config
$specs = $config['sizes'][$derivative];

// Create a new Imagick object
$image = new Imagick();

// Ping the image
$image->pingImage($source_path . '/' . $filename);

// Read the image
$image->readImage($source_path . '/' . $filename);

// Resize, by width & height OR width OR height, depending what's configured
$image->scaleImage(
    (isset($specs['width'])) ? $specs['width'] : 0,
    (isset($specs['height'])) ? $specs['height'] : 0
);

Note that the scaleImage function can accept a width, height, or both.

Now that we’ve resized the image appropriately, we need to do two things.

First, we need to save the image to the appropriate location, which means that any subsequent request will just grab the image itself – bypassing our resizing function.

Second, we still need to honour the original request which means outputting the image, setting the appropriate headers along the way.

// save the file, for future requests
$image->writeImage($destination_path . '/' . $filename);

// set the headers; first, getting the Mime type
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($finfo, $destination_path . '/' . $filename);
$app->response->headers->set('Content-Type', $mime_type);

// Get the file extension, so we know how to output the file
$path_info = pathinfo($filename);
$ext = $path_info['extension'];

// output the image
echo $image;

// Free up the image resources
$image->destroy();

That’s all there is to it! I’ve cut a few corners for clarity, as there are a few things you’d need to think about in production:

  • sanity checking the configuration
  • ensuring that the files are indeed images
  • better error handling, generating an error image in certain circumstances
  • track any changes in the image derivative configuration, and regenerate the images as appropriate

Outputting Images

I’ve deliberately included the media queries in the server-side configuration; because Picturefill requires quite a lot of HTML to operate, generating it is probably a good candidate for a view helper. That’s outside the scope of this article, but let me know in the comments or submit a pull request at Github if you come up with something suitable.

Summary

In this article I’ve described a possible solution to the issue of adaptive images by combining server-side, on-demand creation of image derivatives designed to be used with the Picturefill Javascript library. In time, a third-party Javascript library may well become redundant as standards such as srcset evolve, but the need for server-side solutions is likely to remain.

Of course there’s no need to just use this code for using Picturefill or similar – it could also be used in, say, a content management system to generate different versions of an image for different displays. Can you think of anything else?

Frequently Asked Questions (FAQs) about Responsive Images Using Picturefill & PHP

How does Picturefill work in creating responsive images?

Picturefill is a JavaScript library that enables the use of the picture element and associated features in browsers that do not yet support them. It works by loading the appropriate image file based on the size of the user’s viewport. This means that smaller devices will download smaller image files, which can significantly improve performance and reduce data usage.

What are the benefits of using PHP with Picturefill for responsive images?

PHP can be used to automatically generate the different image sizes required by Picturefill. This can save a lot of time and effort compared to manually creating and managing multiple versions of each image. PHP can also be used to serve the correct image based on other factors, such as the device’s pixel density or the user’s network conditions.

How can I implement Picturefill in my website?

To implement Picturefill, you need to include the Picturefill.js file in your HTML, and use the picture element (or the srcset and sizes attributes on img elements) to specify your images. You can then use PHP to generate the different image sizes and serve the correct one based on the user’s viewport size.

What are the limitations of using Picturefill and PHP for responsive images?

One limitation is that Picturefill relies on JavaScript, which may not be enabled or fully supported in all browsers. Additionally, PHP requires server-side processing, which can increase server load and potentially slow down your website if not properly optimized.

How does Picturefill compare to other methods of creating responsive images?

Picturefill offers a more flexible and comprehensive solution than many other methods. It allows for multiple image sources to be specified, which can be selected based on a variety of factors, including viewport size, pixel density, and network conditions. However, it does require more setup and maintenance than simpler methods, such as using CSS to resize images.

Can I use Picturefill with WordPress?

Yes, Picturefill can be used with WordPress, although it may require some additional setup. There are also several plugins available that can simplify the process, such as the ShortPixel Adaptive Images plugin.

How can I test if Picturefill is working correctly?

You can test Picturefill by resizing your browser window and observing whether the correct image is loaded at different sizes. You can also use developer tools to inspect the image element and see which source has been selected.

What is the impact of using Picturefill on SEO?

Using Picturefill can potentially improve your SEO by improving page load times and providing a better user experience on different devices. However, it’s important to ensure that your images are properly optimized and that the correct image is being served to each user.

Can I use Picturefill with other JavaScript libraries or frameworks?

Yes, Picturefill can be used with any JavaScript library or framework. It does not have any dependencies and should not conflict with other scripts.

What is the future of responsive images and Picturefill?

As more browsers begin to natively support the picture element and associated features, the need for libraries like Picturefill may decrease. However, the flexibility and control offered by Picturefill, especially when combined with a server-side language like PHP, will likely continue to be valuable for creating responsive images.

Lukas WhiteLukas White
View Author

Lukas is a freelance web and mobile developer based in Manchester in the North of England. He's been developing in PHP since moving away from those early days in web development of using all manner of tools such as Java Server Pages, classic ASP and XML data islands, along with JavaScript - back when it really was JavaScript and Netscape ruled the roost. When he's not developing websites and mobile applications and complaining that this was all fields, Lukas likes to cook all manner of World foods.

Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week