How until was built: live Open Graph images on Cloudflare Workers, in one evening

· Fábio Costa

Countdown pages are a solved problem. What was missing, at least for me, was a countdown link: something you paste into a family WhatsApp group or a team Slack and the preview card itself says "108 days until Christmas 2026". That is the whole product. Type a name and a date, get a link, send it.

What it does

The stack

One Cloudflare Worker, one D1 database, static assets served by the same Worker, a custom domain attached from wrangler.toml. No framework; the HTML is template strings, the CSS is inlined, the client script is about 60 lines that tick the numbers and fill in the browser's time zone. Everything sits inside the free tier.

The preview image is made with workers-og, which bundles satori (HTML/CSS to SVG) and resvg (SVG to PNG) as WebAssembly. The Worker renders the image per request and caches it for an hour. The image URL carries the current day count as a query string, so when a platform re-scrapes the page it also refetches the image.

Styling without a canvas

The tempting build was a Canva-style editor. It would have been heavy, awkward on a phone, and the preview image has to be re-rendered on the server, which a free-form canvas makes hard. Instead the look is a small JSON object: a preset, three colours, a font pairing, a number style, a texture, a radius, an emoji, a line of text. The server turns it into CSS custom properties on the page and into a satori layout for the image, and the browser applies the same maths live while you fiddle. The photo trick is forty lines: draw the image on a 64-pixel canvas, bucket the colours, pick the dominant one for the background and the most saturated other one for the accent.

Payments without webhooks

The Pro upgrade uses hosted Stripe Checkout. The session is created with the countdown id in its metadata; after Stripe redirects back, the Worker retrieves the session with the secret key and only then applies the upgrade, recording the session id so a reload cannot apply it twice. No webhook secret is needed for that, which mattered because this was built while the account owner was away. A webhook handler can be bolted on later using the same two functions.

Things that went wrong

Honest numbers

Launch day. Countdowns created: a handful, all mine. Paid upgrades: one, in Stripe's test mode with a test card, to prove the flow. Live-mode sales: zero. This paragraph will be updated as real numbers come in.

Who wrote the code

Most of it was written by an AI coding agent (Claude Code) working autonomously from a one-page brief I wrote, including the tests and this post. I am the human who owns the project and the Stripe account, and I am posting it as my own work because it is.

Make a countdown