Screenshots & Assets2 min read

Capturing 35 web apps' result screens with headless Chrome

No Playwright dependency, no manual clicking. Just the headless Chrome CLI at the right viewport width — plus the one width value that stops two-column result cards from clipping.

#headless-chrome#screenshots#automation#nextjs
Concept diagram: headless Chrome capture
A concept diagram summarizing the post.

I needed clean mobile captures of the result screens across ~35 small web apps. No browser-automation library installed. Turns out the headless Chrome CLI is enough.

The command

"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
  --headless=new \
  --window-size=540,1170 \
  --force-device-scale-factor=2 \
  --virtual-time-budget=9000 \
  --screenshot=out.png \
  "https://app.example.com/ko/result/..."

--headless=new runs the current headless mode (JS renders, so Next.js pages hydrate). --force-device-scale-factor=2 gives a crisp 2× image. --virtual-time-budget waits for the page to settle before the shot.

The width gotcha

My first captures at 440px (and 390px) clipped the result cards — the two-column layouts (a number on the left, a percentile on the right) ran off the edge. 540px was the answer (≈1080px at 2×): the card fits whole. If your result screen is a wide card, capture wider than a phone and let the shot breathe.

The unlock: result pages open by URL

The bigger win was realizing many apps expose their result pages directly by URL (e.g. /ko/sign/aries, /ko/type/o). That meant no input flow to automate — I could point headless Chrome straight at the result and capture it, skipping form-filling, hidden-result problems, and stale-design issues all at once.

For icons, remember ffmpeg can't take SVG directly — rasterize the app's icon.svg to PNG (cairosvg) before compositing.

The honest part

I reached for "install a browser automation framework" first, out of habit. I didn't need it. The tool already on the machine, pointed at a URL that already renders the thing I want, did the job. Before adding a dependency, check whether the thing you need is a URL and a flag away.

Related