GD, ImageMagick, libvips: the image engine, that decision we make by default

There are infrastructure choices we never really make, we just inherit them. The image processing engine is one of them. Most PHP developers never chose GD: it was there, compiled into PHP, so they used it, and the matter ended there. That is understandable, and it is a mistake, because this invisible component actually decides the speed of your back office, the memory bill of your server, and the quality of the images your visitors see.

The question turns urgent the moment you leave the brochure site for a real pipeline: an e-commerce media library where merchants upload batches of heavy photos that must be declined into several responsive sizes and several modern formats. There, the choice of engine no longer shows up in milliseconds, it shows up in minutes of waiting, in a sweating server, and sometimes in a server that falls over. So let us look seriously at the contenders, and at why one of them deserves, more often than not, to become your deliberate default.

The contenders, and the ones we forget

Three libraries dominate the PHP landscape, and a fourth deserves naming because everyone forgets it.

GD is the historical default, shipped with most PHP installations. It does the basics (resize, crop, convert) and stops there. Its limits are real: no color space handling, and it drops EXIF metadata on encoding. It is the tool for quick fixes, not for demanding production.

ImageMagick, reached in PHP through the Imagick extension, is the Swiss Army knife. It can do just about everything: composition, drawing, effects, hundreds of operations. It is the full arsenal, and it generally yields better results than GD. Its power is also its weakness, as we will see.

libvips is the modern contender, and the hero of this article. Built for speed and frugal memory use, it aims not for exhaustiveness but for the batch processing pipeline: decode, resize, convert, re-encode, fast and without waste.

And the one we systematically forget: GraphicsMagick, a fork of ImageMagick born twenty years ago to be lighter, more stable in its API, and faster on certain operations. It still exists, it is solid, but its community has withered and its support for recent formats lags behind. Citing it is honest; recommending it today, rarely.

The architectural difference that explains everything else

If libvips crushes the competition on performance, it is not a detail of optimization, it is a fundamental difference in design.

GD and ImageMagick, in their classic operation, load the entire image into memory before processing it. A JPEG of 6000 by 4000 pixels is 24 million pixels decompressed and laid out in RAM, then manipulated. It is simple, it is intuitive, and it blows up under load.

libvips works differently: in a stream, on demand. It processes the image by regions, in strips, without ever loading it whole, and it parallelizes automatically across cores. The result is dramatic and measurable: on resizing, libvips is typically several times faster than ImageMagick and uses a fraction of the memory, the gap widening as images grow larger. The documentation of Intervention Image, the PHP library that abstracts all of them, says it plainly: libvips outperforms the standard extensions that are GD and Imagick.

This streaming architecture is exactly what keeps a server standing when a merchant uploads twenty 60-megapixel photos in a row. Where GD would want to load 250 MB into RAM per image, libvips streams at a near constant footprint. The difference is not comfort against discomfort, it is staying up against falling over.

Speed and memory: the counterintuitive truth

We need to put one false idea to rest: the time it takes to generate a thumbnail depends first on the source image, not on the dimensions of the thumbnail you are producing. Generating a thumbnail means decoding the source, resampling, re-encoding. The decode processes every input pixel; the resampling reads most of them; only the final step, encoding the output, depends on the target size, and it is negligible. Producing a 150-pixel or a 400-pixel thumbnail from the same source therefore costs almost the same. What changes everything is the source: a thumbnail drawn from a 60-megapixel original costs tens of times more than one drawn from a 2-megapixel image.

On top of this sits a decisive optimization that GD ignores: reduced decoding at read time. The JPEG format allows decoding directly at half, a quarter, or an eighth of its size, without ever materializing the full-resolution image. libvips and ImageMagick exploit this shrink-on-load; for a large source toward a small target, the input cost collapses. GD, by contrast, decodes everything at full resolution before reducing. That is precisely why it crumbles on large images where libvips stays light.

Enough of the qualitative, let us get to the numbers, because they are eloquent. libvips’s own author benchmark, on a large image, measures a peak memory footprint on the order of 200 MB for libvips against roughly 3 GB for ImageMagick, a factor of 15. A more recent and reproducible benchmark, run under PHP 8.4 by the libvips image module for Drupal on an 8000 by 5333 pixel JPEG source, gives convergent orders of magnitude: libvips about three times faster than GD to produce a thumbnail, and radically faster than ImageMagick, which always decodes the whole image first, while using two to four times less peak RAM than the latter. The most telling effect is cumulative: on a 20-megapixel upload with ten sizes to generate, libvips produces all ten in the time ImageMagick needs for one. In real production, Criteo’s engineering team documented image processing roughly 230% faster after migrating from ImageMagick to libvips.

That this benchmark comes from the Drupal ecosystem is itself worth noting. Let us be precise, because the nuance matters: Drupal core’s default toolkit remains GD, and libvips is not built into the core. But there is now a dedicated and well-supported libvips module that plugs directly into Drupal core’s image system as a first-class alternative to GD and ImageMagick. When a CMS this entrenched in historical PHP gives itself a first-class libvips toolkit, it is one more sign that the PHP world is shifting, quietly but surely.

Here is the recap, bearing in mind that any benchmark depends on the machine, the version, and the format:

CriterionGDImageMagicklibvips
Ships with PHPYesNo (Imagick extension)No (system dependency)
Memory footprintHighVery highLow (streaming)
Speed on large sourcePoorPoor (decodes everything)Excellent
Resampling qualityMediocreGoodGood (Lanczos)
ICC color managementNoYesYes
Native AVIF / WebPPartialYesYes
Feature breadthMinimalMaximalPipeline-focused
Sweet spotQuick fixesCreative manipulationMass generation

A note of honesty is in order, so as not to oversell: libvips’s advantage is not universal. On very small images, its streaming architecture and thread scheduling introduce overhead that does not pay off, and it can then be slower and hungrier than ImageMagick. Users have measured it: on a thumbnail generated from a source of a few hundred kilobytes, ImageMagick can come out ahead. libvips’s gain shows up on large sources and at scale, exactly the terrain of an e-commerce media library, not on the isolated thumbnail of an avatar. Choosing libvips to resize three small icons is solving the wrong problem; choosing it to ingest batches of heavy photos is aiming true.

The honest flip side: when ImageMagick keeps the edge

Let us be fair, because a blind panegyric is worthless. libvips does less than ImageMagick, and that is by design. If you want to compose dynamically, overlay a complex watermark, draw, do elaborate montage, or apply exotic effects, ImageMagick has a far broader repertoire. For the standard e-commerce pipeline (resize, crop, convert, optimize), libvips covers everything. For creative manipulation, ImageMagick remains more complete.

The reasonable conclusion is therefore not “libvips everywhere, throw out the rest.” It is libvips on the hot path, the one that generates thumbnails on the fly or in batches, with ImageMagick kept on hand for the rare and complex cases. GraphicsMagick, for its part, is justified only in very particular contexts where its historical lightness matters more than its lag on modern formats.

libvips reaches far beyond the PHP world

One sign never lies about a library’s worth: what other ecosystems do with it. And here, libvips has already won a battle that many PHP developers do not realize.

In the Node.js world, the reference image processing library, the one everyone recommends, is called Sharp. Yet Sharp is nothing other than libvips dressed in JavaScript: it is the same C engine running underneath. The best tool of a competing ecosystem is therefore your PHP option. On the Python side, the dominant equivalent is Pillow, heir to PIL, comparable to ImageMagick in philosophy. And the on-demand image generation services that set the standard, like imgproxy or imaginary, are themselves built on libvips, while Thumbor relies on Pillow. When an engine becomes the foundation of the reference tools of three ecosystems, it is no longer a fashion, it is an engineering consensus.

The concrete case: an e-commerce media library

Let us get down to the real world, because that is where theory pays off or backfires. Picture a media library that must ingest batches of merchant photos, draw several responsive sizes and several formats from them, without bogging down the upload or saturating the server.

The right pipeline does not generate ten thumbnails per file synchronously after each upload: that is the recipe for a freeze, the massive-upload nightmare every merchant knows. It stores the original, derives a working image once at a reasonable resolution (say 2500 pixels on the long side), and then generates each derivative on demand, from that working image and never from the heavy original. This rule follows directly from the truth stated above: the cost is in the source pixels read, so you read a light source.

In this pipeline, libvips is the right engine for three cumulative reasons. Its memory frugality absorbs heavy uploads without blowing up the RAM. Its native AVIF and WebP support directly serves your strategy of lightweight images. And its resampling quality produces crisp thumbnails where GD would smear. A useful caveat in passing: AVIF encoding is CPU-expensive, so multiplying tiny AVIF thumbnails can become your real bottleneck, far more than reading the source. It is a trade-off to watch, independent of the engine chosen.

In PHP, concretely

Two paths are open to you. The native extension, exposed in PHP by the jcupitt/vips library (a composer require jcupitt/vips once libvips and the FFI extension are in place), gives you direct access to the API, the most performant route. Or, more comfortably, you go through an abstraction layer: Intervention Image, in version 3, offers an official libvips driver. Mind the package name, it is exactly the kind of detail people get wrong: the core stays intervention/image, and the driver is added via composer require intervention/image-driver-vips, alongside the GD and Imagick drivers already included. Let us insist, because this is the real turning point: this driver is now official and mature, maintained by the project itself, and it is probably the factor that will democratize libvips in the PHP world. Many developers still do not know it exists and keep believing the choice is limited to GD or Imagick, because that was true until version 3. Today you write your processing code once, against the abstraction, and you switch engines with a single line of configuration. Elegant: GD in local development if installing libvips puts you off, libvips in production for performance, without touching the rest of your code.

One friction to anticipate, the only real one: libvips is a system dependency, the C library plus its PHP extension, not a mere Composer package. On infrastructure you control, it is a trivial install, but it is one more brick to provision and document, where GD is often already compiled.

And there is a specific trap that warrants a warning, because it costs hours: the libvips packages shipped by default by distributions, especially in containerized environments (Docker on an Alpine base, or a slightly older Ubuntu LTS), are frequently compiled without AVIF and HEIC support. You install libvips, everything seems to work, and you discover in production that AVIF conversion fails silently because the encoding delegate is missing. Concretely, AVIF writing depends on the libheif encoding plugin (the AOM plugin), which is not always bundled in the base package. The Drupal module documents this plainly: since all its default image sizes include an AVIF step, generation fails outright if that plugin is missing. The fix: check the delegates actually compiled in (a vips --vips-config will tell you), use a recent base image where support is present, or compile libvips yourself with the right delegates. Apply the same check to libheif if you want to accept HEIC photos from iPhones, which will make up a huge share of mobile uploads: support for them is not automatic, it depends on the build. This is the kind of verification to put on your provisioning checklist, all the more so if you redeploy the same stack from client to client.

Why we missed it for years

I owe a confession: I worked alongside GD and ImageMagick for years without ever stumbling on libvips, even while actively looking for an alternative. And on closer inspection, it was not a lapse of attention, but the product of several converging factors worth naming, because they explain how an excellent tool can stay invisible for so long.

First paradox: libvips is not young, it is old. Born in the 1990s in the very particular context of heritage image processing, the analysis of paintings and manuscripts in museum projects, it spent two decades in a scientific niche, entirely off the radar of web development. Its age was never a mainstream age. We did not miss it because it was immature, we missed it because it was in another room of the house.

Second factor, the most decisive: the bridge to PHP was long the weak link. The C core was excellent long ago, but comfortable access from PHP is recent. The modern, maintained php-vips extension is relatively new, and above all its integration into the tool that most PHP developers actually use to manipulate images, namely the official libvips driver of Intervention Image, only arrived with version 3. Before that, when you looked for an alternative through the usual channels, libvips literally did not appear in the menu of the tool you had in hand. We were not searching badly, the tool did not list it.

Third factor, the bias of content seniority. Type “PHP image resize” for fifteen years and the web sends you back an ocean of GD tutorials, then ImageMagick, which carry a massive teaching footprint. libvips, confined to its niche, has almost no presence in that general corpus. Even an honest search keeps returning the same two names. The alternative existed, but it was not indexed in the conversation of web developers.

Fourth factor, and the most humbling to admit: GD and ImageMagick were enough. libvips’s advantage is not functional, it does roughly the same basic operations, it lies in performance and memory. And an advantage you only see under load sells far worse than a visible functional gap. As long as you are resizing a handful of images on an ordinary site, GD does the job and the question never comes up. libvips becomes decisive only at scale: upload batches, 60-megapixel sources, mass generation of derivatives. That is exactly the context you meet when building a real e-commerce engine, and not before. The pain that makes libvips indispensable is recent in the path of many of us. We do not discover it too late, we discover it at the precise moment the project finally gives us a reason to look for it.

Conclusion: choosing, rather than inheriting

The verdict is sober. For the hot path of a serious site, generating thumbnails and converting formats, libvips should be your default, for its speed, its memory frugality, and its output quality. ImageMagick remains valuable for the complex creative work that falls outside the standard pipeline. GD is justified only for quick fixes, local development, or a legacy you would rather not touch.

There remains the argument always raised in GD’s favor: it is universally installed. That argument holds only if you do not control your infrastructure. And controlling your stack is precisely about refusing to let a historical default decide in your place. On a server you administer, provisioning libvips is not an obstacle, it is a decision. And an engineering decision made consciously, weighing architecture, performance, and quality, is always worth more than a choice you inherited because it happened to be there already. The image engine is not a detail: it is a chance to take back control of a brick that too many people let their tool choose for them.


Écrivez quelques éclats d'âme...

Dans l'ombre vacillante d'une chandelle, où les murmures du vent se mêlent aux secrets d'un vieux parchemin, je vous invite à tisser une toile de mots. Écrivez quelques éclats d'âme – rêve, étoile, abîme, étreinte, brume – et laissez-les danser sur la page, comme des lucioles dans une nuit d'encre. Que diriez-vous de les entrelacer dans une phrase, un souffle, une histoire ?

Subscribe
Notify of
guest
0 Commentaires
Oldest
Newest Most Voted