Three Interfaces, One Image, No Puppeteer
How to composite screenshots programmatically when you don't have a headless browser.
The newsletter argument needed a single image. Three different tools processing the same weather data—terminal output, a planning view, a dashboard—shown side by side. Not described. Shown.
Headless browsers are the obvious choice for capturing interfaces programmatically. But Chromium wasn’t available, Playwright wasn’t installed, and this was midnight before a publish deadline.
What was available: screencapture (macOS), a screen region capture tool, and Python.
What I Tried
The workflow ended up being three steps. First, capture each interface separately while it was live in the browser—screencapture grabs a window or region on command. Second, crop the browser chrome out of each screenshot in Python: load the PNG, slice the content area out of the full window capture. Third, composite the three cropped panels side by side using Pillow’s Image.new() and paste().
The sizing math is the fiddly part. You need to know how wide each panel is, calculate the total canvas width, and place each panel at the right x offset. If the panels are different heights you pick a target height and either crop or pad. I cropped.
What Actually Happened
It worked. The final image was 34KB. No external dependencies beyond Pillow.
The interesting thing: screencapture requires the windows to actually be visible on screen, in focus, and sized correctly. I had to manually arrange all three browser windows before running the capture commands. That felt like a limitation, but it also meant the screenshot was exactly what a human would see—no rendering artifacts, no headless quirks where fonts or layouts shift.
The Part Worth Keeping
Puppeteer and Playwright are powerful. They’re also overkill for “I need to capture three things I can see on my screen right now.” The programmatic capture and composite workflow handles that use case with zero infrastructure overhead.
The workflow: capture visible windows → crop chrome → composite → optimize → deploy. It doesn’t change when the content does. If you’re building visual comparison content and don’t have a headless browser in your stack, you don’t need one.