Skip to content
Back to all work
Application

ScreenShooter

Retakes every screenshot of my projects for this portfolio, so the pictures never fall behind.

Year
2026
Status
Live
Role
Solo: design, front end, back end, deployment.
ScreenShooter

Screenshots of an app go stale the moment the app changes, and refreshing them by hand meant an evening of opening, cropping and copying files for a single project. So I built a tool that takes them for me. It logs in, walks through each page, tab and menu, and shows me the new picture beside the old one, and nothing reaches the site until I accept it. Every screenshot on this site was retaken by it.

Stack

  • Next.js
  • React
  • TypeScript
  • Tailwind CSS
  • SQLite
  • Playwright
  • sharp
  • pixelmatch
  • Docker
  • Claude Code
  • MCP
  • Git
  • Cloudflare Tunnel

In practice

  • One tile per project and one button that retakes every picture of it: each page, tab, menu and modal the portfolio shows. It signs in the way each app needs: as a guest, by filling in the form, by pressing a demo button, or with a session recorded once for an account behind 2FA.
  • A recipe is written the way you would say it: "click the settings tab", "scroll to the containers section", "click button akceptuje". Typos, swapped letters and missing Polish characters are forgiven.
  • Every new picture sits beside the one on the site, with a wipe, a side-by-side view and the change in per cent. Nothing goes anywhere until it is accepted.
  • A shot that names an element is taken three ways at once: the whole window, the element alone, and the element framed in the gallery's 16:11. The choice is made afterwards, on the card. Private details are blurred by drawing rectangles on the picture, and the blur is applied again on every later retake.
  • Live mode is for views too fiddly to write down: a real browser on the server, already signed in, driven with my own mouse and keyboard, with a button that saves what it shows at full quality.
  • Claude can walk an app and propose recipes. Every proposal is replayed from a fresh page before it is added, so a recipe that only worked halfway through a session never gets in.
  • Groups and their order are dragged on the board and written into the site's project list, with headings in English and Polish (the missing language is translated by Claude). Pictures nothing uses any more are offered for removal, and one project can span several sites, so a shot on a sister domain lands in the same gallery.
  • Commit and push are separate buttons from taking pictures, and are signed as ScreenShooter, so the history shows at a glance that a commit changed only screenshots. On shared hosting that rations requests it learns the right pace from the first 429 and remembers it, and a picture hit by one is retaken rather than kept half-drawn.

How it is built

  • Next.js 15 on the App Router, React 19, TypeScript and Tailwind 4, over SQLite through better-sqlite3, in one container built on Microsoft's Playwright image, so Chromium arrives with its fonts and libraries.
  • Pictures are taken at twice the pixel density, scaled down with Lanczos and saved as WebP at quality 95 with smart subsampling, which keeps coloured text on dark cards from smearing. Changes are measured with pixelmatch at a shared size.
  • Recipe steps go through a small parser rather than a fixed syntax: synonyms for verbs, edit distance with transpositions for typos, Polish letters folded both ways, the element's kind read before or after its name, and headings searched first for "the X section".
  • Explore runs Claude Code headless against a zero-dependency MCP server of my own, which offers only look, click, scroll and propose. Clicks whose label reads like save, delete or sign out are refused before they happen.
  • Live streams the server's browser as JPEG frames and sends clicks, wheel and keys back as coordinates in its own pixels. A file picker the page opens is answered from the visitor's own computer, since the server has no desktop to show it on.
  • Embedded maps draw only while on screen, so in a full-page picture each one is photographed in view and pasted into place. Lazy frames load early, and the capture CSS stays out of iframes, where it froze the map's tiles.
  • Request pacing goes through the Fetch domain of Chrome DevTools rather than Playwright's routing: routing turns the browser cache off, and every page would fetch all its scripts again, which is exactly the burst the hosting counts.
  • The site's project list is JSON shared with an admin panel. ScreenShooter pulls before every write, edits only its own project's object and writes the file back byte for byte as the panel does, so a change is a one-line diff.
  • Git gets its token as a one-off header, never in a remote URL that would end up in config, and error messages never repeat the command line. A rejected push replays the screenshot commits on top and keeps the fresh pictures.
  • Long jobs run in the background and are polled, because the Cloudflare Tunnel cuts any request at a hundred seconds. Stored passwords are encrypted with AES-256-GCM and never leave the server, not even in a project export.

Projects

A project

Publishing

Live

Sign in

Let's build something together

Got something in mind? Even half an idea is enough to start a conversation. Every message gets an answer, even when the honest answer is that someone else suits it better.

An answer within 24 hours, with a first estimate and how I would approach it.

Based in Belgium, working remotely with clients in English and Polish.