# Optimizing Images in PHP: Handling GIFs Without Breaking Animation

After reading [Steven Fox](https://pinkary.com/@stevenfox)’s post on [**A Hack for Efficient Infinite Scrolling in Your Livewire App**](https://steven-fox.com/articles/a-hack-for-efficient-infinite-scrolling-in-your-livewire-app) I started to think on what’s been bothering me most on the Pinkary platform. As you can guess it was the broken gifs! So I’ve decided to get to the bottom of this.

<mark>This example is based on the Pinkary project, but as long as your environment has the </mark> [<mark>Imagick extension</mark>](https://www.php.net/manual/en/book.imagick.php)<mark>, it can be used in any PHP code.</mark>

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">If you don’t know what Pinkary is it’s a fully open sourced microblog-ish linkshare-ish platform and the community is full of php and laravel developers. <a target="_blank" rel="noopener noreferrer nofollow" href="https://github.com/pinkary-project/pinkary.com" style="pointer-events: none">https://github.com/pinkary-project/pinkary.com</a></div>
</div>

I don’t want this post to turn into a 'what and why' explanation instead of focusing on the 'how.' But just to give a quick overview in case you're unfamiliar with image optimization: <mark>it's all about reducing the file size of an image without losing quality, making it more suitable for the web.</mark> Fortunately, PHP provides powerful libraries like **Imagick** (the PHP extension for ImageMagick) that enable us to optimize images on the server side.

For me, the most important effects are the impact on page speed and bandwidth usage, but it's really useful from many aspects. Google it. Please.

Let’s take a look at what we’ve been using in [Pinkary](https://pinkary.com) so far (I’ll update if my PR gets approved).

`\App\Livewire\Questions\Create::optimizeImage` :

```php
public function optimizeImage(string $path): void
{
    $imagePath = Storage::disk('public')->path($path);
    $imagick = new Imagick($imagePath);

    $imagick->resizeImage(1000, 1000, Imagick::FILTER_LANCZOS, 1, true);

    $imagick->stripImage();

    $imagick->setImageCompressionQuality(80);
    $imagick->writeImage($imagePath);

    $imagick->clear();
    $imagick->destroy();
}
```

This function does a great job for static images (JPEG, PNG, etc.), but it won't work for animated visuals like GIFs since they have multiple frames. If a GIF is passed into this function, it will resize and optimize only the first frame, turning the animation into a single, static image.

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">So, we had to come up with a solution to detect if the visual, in our case the image, has multiple images or not, and then decide whether to optimize all frames or just the static visual if that’s the case. But how do we understand if the given image has frames or not? It’s simple. The Imagick extension provides a wide set of functions and features to detect and manipulate almost everything about the image, such as frames.</div>
</div>

Even though we knew it was a multiple-frame image, we needed to prepare the image object to optimize every frame. Otherwise, we would encounter the same issue of optimizing only the first frame.

Now we have another feature to use for this it’s [`Imagick::coalesceImages`](https://www.php.net/manual/en/imagick.coalesceimages.php). It returns a new Imagick object on success which all frames prepared to be processed like:

```php
$image = new Imagick($path);

$image = $image->coalesceImages();

foreach ($image as $frame) {
    //handle the frame optimization process
}
```

Let’s see the final version of the optimization and examine how it works  
`\App\Livewire\Questions\Create::optimizeImage` :

```php
public function optimizeImage(string $path): void
{
    $imagePath = Storage::disk('public')->path($path);
    $imagick = new Imagick($imagePath);

    // Check if the image is animated by looking at the number of frames
    if ($imagick->getNumberImages() > 1) {
        $imagick = $imagick->coalesceImages(); // Prepare to work with all frames

        foreach ($imagick as $frame) {
            $frame->resizeImage(1000, 1000, Imagick::FILTER_LANCZOS, 1, true);
            $frame->stripImage();
            $frame->setImageCompressionQuality(80);
        }

        $imagick = $imagick->deconstructImages(); // Optimize the animation
        $imagick->writeImages($imagePath, true); // Save all frames back into the file
    } else {
        // Process the image normally if it is not animated
        $imagick->resizeImage(1000, 1000, Imagick::FILTER_LANCZOS, 1, true);
        $imagick->stripImage();
        $imagick->setImageCompressionQuality(80);
        $imagick->writeImage($imagePath);
    }

    $imagick->clear();
    $imagick->destroy();
}
```

Before:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1726427238304/14cf0e8a-5469-4527-add6-f046890d6987.png align="center")

End Result:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1726427740557/18a38f80-75c1-4a6f-974f-424a352d9fec.gif align="center")

That’s how you can optimize every frame of your animated visuals on your php application. I learned a lot while working on it, and I hope you did too.

<div data-node-type="callout">
<div data-node-type="callout-emoji">😈</div>
<div data-node-type="callout-text">If you are a PHP developer and still don’t have a <a target="_blank" rel="noopener noreferrer nofollow" href="https://pinkary.com" style="pointer-events: none">pinkary.com</a> account, I curse you. See you next time.</div>
</div>
