My site was quietly destroying every photo I uploaded

September 2, 2026 ยท Build log

The project cards on my home page each have a photo. For a while, the card for Hangeulmaru โ€” my Korean school app โ€” showed a thin horizontal strip of what appeared to be the middle of a screenshot. No title, no header, no context. Just a sliver.

I'd uploaded that photo myself, looked at the result, thought "huh, that's not what I picked," and moved on. Twice. It took a third upload before I asked the obvious question: where did the rest of my picture go?

Aspect ratio, and the thing nobody tells you

The photo box on each card is a 16:9 rectangle โ€” wide and short, like a TV screen. My source image was a screenshot from my phone, which is tall and narrow. Those two shapes are not compatible, and something has to give.

What my site did was the standard thing: fill the box, keep the proportions, and cut off whatever hangs outside. Applied to a tall phone screenshot, that means it keeps a horizontal band from the middle and discards the top and bottom entirely. It's the sensible default, it's one line of CSS, and for a tall image it is quietly catastrophic โ€” because the interesting parts of a screenshot are almost always the top and the bottom.

Nothing was broken. Nothing errored. The image was uploaded perfectly and displayed exactly as instructed. The software did what I told it, and what I told it was wrong.

The version I tried first, which was worse

My first instinct was to make the image fit instead of fill โ€” show the whole picture, letterbox the empty space. That's technically lossless and it looked terrible: a tall screenshot squeezed into a wide box becomes a postage stamp floating in a sea of background, and now every card on the page has a different amount of dead space.

The honest realization was that cropping wasn't the problem. Cropping without being asked was the problem. Something has to decide which part of a tall photo survives, and the right answer is the person who chose the photo โ€” not a CSS rule, and not me finding out after saving.

What I built

Now, when I pick a photo in the editor, the image appears with a 16:9 crop box laid over it. I drag the box to move it, drag the corners to resize it, and the bright area inside the box is exactly what the card will show โ€” no interpretation, no surprise. The crop gets applied when I hit save, so my last adjustment is always the one that counts, and the result is capped at 1200 pixels wide because a card on a web page has no use for a 4000-pixel photo.

The feature took an afternoon. Two of the bugs I hit along the way were far more interesting than the feature itself.

Bug one: phone photos arrived lying on their side

Some photos loaded into the cropper rotated 90 degrees, even though they were upright everywhere else โ€” in my photo library, in Finder, in every preview I'd ever seen.

Here's what I learned. When your phone takes a photo, it doesn't always rotate the pixels to match how you held the camera. Often it stores the pixels in one fixed orientation plus a small note attached to the file that says "display this rotated 90 degrees." That note is called EXIF orientation. Photo apps read the note. My raw canvas code did not, so it drew the pixels as they were literally stored โ€” sideways.

The fix was choosing a decoding method that honors the note, so the image lands upright before anything else touches it. But the lesson is the one I keep relearning: a file is not the same thing as what you see when you open the file. There's a layer of interpretation in between, and when you write code you sometimes drop below that layer without realizing it.

Bug two: transparent logos came out black

A PNG with a transparent background โ€” a logo, say โ€” came out of the cropper with the transparent parts rendered solid black.

The reason is that the crop output is saved as JPEG, and JPEG has no concept of transparency. There's no "see-through" value available in the format, so every transparent pixel has to become some actual color. Absent instructions, that color is black.

The fix is called matting: paint the canvas with the background color first โ€” in my case the site's cream โ€” then draw the image on top. The transparent areas pick up the cream instead of black, and the logo looks like it's sitting on the page rather than in a hole. One extra step, and it only exists because of a format limitation from 1992.

What I took away from it

I don't have a computer science background, so most of what I know arrived exactly like this: something looked wrong, I refused to accept it, and the explanation turned out to be a real piece of how the web works. The Hangeulmaru card shows the whole screenshot now.

More of these in the build log, or on Instagram.