This is episode four of What Did I Just Install.
Imagine you are building a photobooth. Someone walks up, strikes a pose, and your software needs to take that image and do something with it. Resize it. Crop it. Overlay some text. Maybe slap a filter on it so the person looks like they are having a better time than they actually are. In Python, there is exactly one way to do this. One library that every image operation, every computer vision pipeline, every thumbnail generator, every machine learning preprocessing step eventually calls. It is called Pillow. And Pillow exists because its predecessor died, and one person decided the corpse was worth carrying.
The original library was called PIL, the Python Imaging Library, and for fifteen years it was the only game in town. If you wrote Python and needed to touch a pixel, you used PIL. But by two thousand ten, PIL had a problem. Its creator had gone silent. The last release was nearly a year old. Python three had arrived, and PIL did not support it. The packaging was a nightmare. And the Python community was left staring at a library that millions of projects depended on, wondering if anyone was going to do something about it.
Someone did. His name was Alex Clark, and he was not a computer vision expert, not an imaging researcher, not a graphics programmer. He was a web developer in Washington DC who was tired of fighting with a broken install process.
To understand why Pillow matters, you need to understand what Python looked like in nineteen ninety-five. Python was three years old. It was a scripting language that people used for text processing, file operations, and gluing other programs together. It had no built-in support for graphics. No way to load a JPEG. No way to resize a photograph. No way to draw a line on a canvas. If you wanted to manipulate an image in Python, you were on your own. You could shell out to an external tool, or you could write C extensions and bind them yourself, which was roughly as pleasant as it sounds.
Fredrik Lundh saw this gap and decided to fill it. Lundh was a Swedish developer based in Linkoping who had co-founded a small company called Secret Labs AB. In the mid-nineties Python world, he was a giant. He wrote the regular expressions module that ships with every copy of Python to this day. He wrote ElementTree, which became the standard way to handle XML. He wrote Tkinter documentation so comprehensive that an entire generation of Python developers learned graphical programming from his website, effbot.org. And in nineteen ninety-five, he created the Python Imaging Library.
PIL was brilliant in its design. It gave Python a clean, intuitive interface for opening images, manipulating them, and saving them in dozens of formats. Load a JPEG, convert it to greyscale, resize it, save it as a PNG. Four lines of code. Before PIL, this required calling out to ImageMagick or writing raw C against the libjpeg headers. After PIL, it was a few method calls. The library supported over thirty image formats, including JPEG, PNG, TIFF, GIF, BMP, and more esoteric ones like PCX and PPM. It handled color space conversions, affine transforms, convolution filters, histograms, and text rendering. It was not just a file format converter. It was a complete imaging toolkit.
And for a decade, it worked. PIL was the answer to every image question on comp.lang.python. Need thumbnails? PIL. Need to watermark photos? PIL. Need to generate captchas? PIL. It became so foundational that major projects built their entire imaging pipelines around it. Plone, the Python content management system, used PIL for all its image handling. Django used it for image fields. Scientific Python used it for loading experimental data. It was everywhere.
Fredrik Lundh was not just PIL's creator. He was, for years, one of the most visible people in the entire Python community. His handle was effbot, and the name tells a story.
In the late nineties and early two thousands, the comp.lang.python newsgroup was the center of the Python universe. If you had a question, you posted it there, and you waited. Except with Lundh, you did not wait. He was there, seemingly at all hours, answering questions with a speed and consistency that made people suspicious. Newbie questions about string formatting. Advanced questions about interpreter internals. Questions about PIL, about XML parsing, about regular expressions, about Tkinter. He answered them all, patiently and thoroughly, often within minutes. The community started joking that he could not possibly be a real person. He had to be some kind of Eliza, they said, referring to the famous early chatbot. An automated response bot for Python questions. The joke became his identity. Eff-bot. Fredrik's bot. The name stuck, and he wore it proudly.
He was not the only one with a bot suffix. Tim Peters, the creator of Python's sorting algorithm Timsort, was called the tim-bot. Alex Martelli, the prolific cookbook author, was the Martelli-bot. But Lundh was the original. The effbot who answered everything.
His website, effbot.org, became an institution. It was not flashy. It looked like it had been designed in nineteen ninety-eight, because it had been. But the content was extraordinary. His Tkinter documentation was the definitive reference for years, better than the official docs, more complete, more practical, full of examples that actually worked. Generations of Python developers learned GUI programming from that site. When it eventually went offline, people mourned it like the loss of a library.
And then, sometime around two thousand nine, Fredrik Lundh went quiet.
There was no announcement. No farewell blog post. No dramatic exit. The last official release of PIL was version one point one point seven, published on November fifteenth, two thousand nine. After that, nothing. No new releases. No bug fixes. No responses to pull requests. No messages on the mailing list. The man who had answered every question for a decade simply stopped answering.
The Python community did not know what had happened. Some speculated he had moved on to other things. Some thought Secret Labs had shifted focus. Some worried about his health. The truth is that nobody outside his personal circle seems to know exactly why he stepped back, and he never explained publicly. He just left, and PIL sat there, unmaintained, like a house whose owner had gone on a trip and never come back.
This might have been fine for a small utility library. But PIL was not small. It was load-bearing infrastructure. Python three had been released in December two thousand eight, and PIL did not support it. As the community slowly migrated to Python three, this became an increasingly serious problem. You could not use the standard imaging library with the current version of the language. Meanwhile, PIL's packaging was archaic. It used a custom build system that predated modern Python packaging tools. Installing it required compiling C extensions against system libraries, and the process was different on every operating system. It was not available on PyPI, the Python Package Index, in any usable form. Third-party repackagings proliferated, each containing slightly different fixes, none of them authoritative.
The library that the Python imaging ecosystem depended on was frozen in time, packaged badly, and maintained by nobody.
Alex Clark was not the person you would expect to rescue Python's imaging infrastructure. He was a web developer and systems administrator in the Washington DC area. He had studied computer science at Loyola University Maryland. In two thousand four, he had founded a consulting company called ACLARK dot NET, working with organizations on open source technology. His world was not computer vision or image processing. His world was Plone.
Plone is a content management system written in Python, one of the oldest and most sophisticated in the open source world. If you have never heard of it, that is fine. Most people have not. But in the mid two thousands, Plone was a serious enterprise CMS used by government agencies, universities, and large organizations. It handled content in multiple languages, had granular access controls, and was built on top of Zope, Python's original application server. And like almost every CMS, it needed to handle images. Which meant it needed PIL.
Clark kept running into the same problem, over and over. Installing PIL for Plone projects was a nightmare. The library was not properly packaged for pip. There was no egg on PyPI, egg being the Python packaging format of that era, predating the wheel format we use now. Instead, you had to download the source from Lundh's website, run a custom setup script, make sure all the right C libraries were installed on your system, and hope. If something went wrong, and something usually did, you were on your own. The man who wrote the library was not answering questions anymore.
Worse, the packaging vacuum had created a mess. Multiple developers had created their own repackaged versions of PIL and uploaded them to various places. Some had setuptools support. Some did not. Some worked on certain operating systems. Some were abandoned. None of them were the real PIL. It was a fragmented landscape of workarounds, each one a monument to a single underlying problem: the original maintainer had disappeared, and the packaging was broken.
On the last day of July two thousand ten, Clark did something simple. He took the PIL source code, applied a set of packaging fixes that another Plone developer named Hanno Schlichting had put together, added proper setuptools support, and uploaded the result to PyPI as version one point zero of a package called Pillow.
That was it. No new features. No bug fixes. No Python three support. Just PIL, packaged correctly, available on PyPI, installable with pip. The entire value proposition of Pillow one point zero was that you could type pip install pillow and it would work.
The name was deliberate. PIL. Pillow. A pillow is soft and comfortable. It is the friendly version of the thing you rest your head on. The tagline Clark chose was "the friendly PIL fork." Not a hostile takeover. Not a replacement. Just a friendlier version of the same thing, one that played nicely with the tools everyone was already using.
Clark's stated goal was modest.
I wanted to foster and facilitate code and packaging improvements through publicized development and community support.
He was not trying to become the maintainer of Python's imaging infrastructure. He was trying to fix an annoying install problem so his Plone projects would stop breaking.
What happened next follows a pattern that recurs throughout open source: a practical fork outlives the thing it forked from. Not because the fork is technically superior, but because the fork is maintained.
Pillow's early releases were strictly about packaging. Get it on PyPI. Make it installable. Make it work with virtualenvs, the isolated Python environments that were becoming standard practice. But once those basics were handled, the community started contributing actual improvements. Bug fixes that had been sitting in PIL's abandoned issue tracker. Support for new image formats. Performance optimizations. And the big one: Python three compatibility.
Python three support arrived in Pillow two point zero in two thousand thirteen. This was the tipping point. PIL could not run on Python three. Pillow could. If you were migrating your project to Python three, and by two thousand thirteen you really needed to be, Pillow was your only option. There was no decision to make.
Linux distributions started switching. Fedora adopted Pillow. Debian adopted Pillow. Ubuntu adopted Pillow. The operating systems that millions of servers and development machines ran on were now shipping Pillow instead of PIL. When your operating system's package manager installs your library by default, you have won.
By two thousand fourteen, the Python Packaging Authority, the group that oversees PyPI and Python packaging standards, suggested that Clark take over the PIL project name on PyPI itself. The old PIL entry, the one that Lundh had controlled, now pointed to Pillow. If you typed pip install PIL, you got Pillow. The fork had not just replaced the original. It had become the original.
This is where the meta-narrative lives, and it is the same one we keep circling in this series. The bus factor. How many people need to be hit by a bus before the project dies?
For PIL, the answer was one. Fredrik Lundh was PIL. He wrote it, he maintained it, he documented it, he answered questions about it. When he stopped, PIL stopped. Not because the code was bad. The code was excellent. But code does not maintain itself. It does not fix its own bugs. It does not add support for new Python versions. It does not update its packaging when the ecosystem changes around it. Code without a maintainer is a corpse. A well-preserved corpse, maybe, but a corpse.
The Python community got lucky with PIL. Someone happened to be annoyed enough to fork it, and patient enough to keep maintaining the fork for over a decade. But luck is not a strategy. For every PIL that gets rescued by a Pillow, there are dozens of critical libraries that simply rot in place, their maintainers having moved on without telling anyone.
What makes the PIL story particularly striking is that Lundh was not just maintaining the code. He was the entire knowledge base. His effbot.org documentation. His comp.lang.python answers. His understanding of the C-level internals, the format-specific quirks, the decisions made in nineteen ninety-five that still shaped the architecture in two thousand ten. When he left, that knowledge went with him.
We had to reverse-engineer not just the code, but the intent behind the code.
On November twenty-fifth, two thousand twenty-one, a message appeared on Fredrik Lundh's private Facebook page. It was written by someone named Ylva Larensson, and it said: "It is with such sorrow and pain that I write this. Fredrik has left us suddenly."
Guido van Rossum, Python's creator, shared the news with the Python developers mailing list on December tenth. The tributes came quickly.
I felt privileged to study his code and read his writing. His sense of humor was a gift to the community.
Others remembered his patience with beginners, his encyclopedic knowledge, the way his answers on comp.lang.python had shaped their understanding of the language. One person noted that he was not just knowledgeable but also patient in explaining basic questions.
The Pillow team dedicated their nine point zero release, published on January second, two thousand twenty-two, to Lundh's memory. In the release notes, they acknowledged that PIL remains, through the Pillow fork, the primary way to interact with images in Python. The library he created in nineteen ninety-five was still, twenty-seven years later, the foundation.
Fredrik Lundh never explained why he went silent. He never published a farewell. He never handed off the project or designated a successor. The silence that began around two thousand nine lasted until his death in two thousand twenty-one. Twelve years. In those twelve years, Pillow went from a packaging fix to the most downloaded imaging library in the Python ecosystem. The fork he never acknowledged outlived him.
Pillow today downloads over two hundred and fifty million times per month. That number puts it in the top tier of PyPI packages, alongside requests and urllib3 and the other infrastructure libraries that the entire Python ecosystem rests on. When you install almost any package that touches images, Pillow comes along for the ride.
The list of projects that depend on Pillow is staggering. Every major machine learning framework. Every computer vision library. Every web framework that handles image uploads. Every PDF generator that embeds images. Every scientific computing toolkit that visualizes data. Matplotlib uses Pillow. OpenCV's Python bindings can use Pillow. Hugging Face Transformers uses Pillow for vision models. Django uses Pillow for image fields. Flask extensions use it. Streamlit uses it. If your Python code touches a pixel, Pillow is almost certainly in your dependency tree.
Pillow itself has relatively few dependencies. It is mostly self-contained C code wrapped in a Python interface, the same architecture Lundh designed in nineteen ninety-five. The C extensions link against system libraries like libjpeg, libpng, zlib, and others, but these are typically provided by the operating system or bundled in Pillow's wheel distributions. It is a dependency that many things depend on, but that itself depends on very little. A foundation stone.
Clark still leads the project, now going by Jeffrey A. Clark on GitHub. He is joined by a small team of core maintainers, most notably a contributor known as radarhere who has become one of the most prolific contributors in Pillow's history. The project received funding support through Tidelift starting in two thousand nineteen, providing income for four part-time developers. It is not a fortune. It is not what a project with a quarter billion monthly downloads deserves. But it is something, which is more than PIL ever had.
If you write Python and your code touches images in any way, Pillow is almost certainly in your dependency tree. Not once. Multiple times. It shows up in web applications that handle user uploads, in data pipelines that preprocess images for machine learning, in content management systems that generate thumbnails, in scientific tools that visualize experimental results, in print automation workflows, in QR code generators, in photobooths and kiosks and ticket systems and dashboards.
The range is staggering. A Django app that lets users upload profile photos. A computer vision pipeline resizing training data before feeding it to a neural network. A script that watermarks photographs for a news outlet. A tool that generates event badges with QR codes. A Jupyter notebook that displays satellite imagery. Every one of those, Pillow. It sits so deep in the Python ecosystem that most developers have it installed in every virtual environment without ever explicitly asking for it.
Every one of those use cases exists because a web developer in Washington DC got tired of a broken install process fifteen years ago and uploaded a properly packaged version of a library written by a Swedish programmer who answered every question on comp.lang.python for a decade and then, one day, stopped. The code that processes those images is, at its core, the same code Fredrik Lundh wrote in nineteen ninety-five. Cleaned up, extended, ported to Python three, maintained by people he never worked with. But his architecture. His design decisions. His vision of what image processing in Python should look like.
The friendly fork is running right now, on servers you have never heard of and in applications you use every day. It will be running when you finish listening to this episode. And it will be running tomorrow, because someone decided that the thankless work of keeping old code alive was worth doing, even when the original author would never know.
That was episode four.
Open a terminal and type pip install pillow. Now open a Python shell and type from PIL import Image. Find any image on your computer and type img equals Image dot open, passing the file path. Then type img dot size to see its dimensions. Now try img dot resize, pass it a tuple like two hundred by two hundred, and call dot save with a new filename. You just loaded, resized, and saved an image in three lines of Python. That is the interface Fredrik Lundh designed in nineteen ninety-five, still working, still elegant, carried forward by the friendly fork.