The Tessera machine: a daily puzzle that runs itself

I used AI to build an automated daily puzzle and a marketing machine around it. New puzzle at midnight, screenshot to five social networks at 08:00, reminder email at 06:00, stats dashboard refreshing itself. I went on holiday for a week. It kept shipping.

AI & Workflows26 May 2026
Published
26 May 2026
Category
AI & Workflows
Reading time
6 min
Tags
AI, Tessera, Automation, Indie, Daily puzzle

The week I went on holiday

I came back from a week away to a phone full of streak notifications from players I'd never met, a dashboard showing 1,140 solves on the day I was eating dinner somewhere with no Wi-Fi, and seven daily posts on five social networks that I had not lifted a finger to publish.

Tessera, the daily word puzzle I've been building, ran itself for the whole trip. New puzzle at UTC midnight. Screenshot posted to X, Facebook, Reddit, Bluesky, and Instagram at 08:00. Reminder email out to subscribers at 06:00. Stats precomputing on a cron so the social blurb for the next morning was ready before I woke up. A small machine, ticking.

This post is the notes on how that machine got built, and what the dashboard looks like now that it's been running for a few weeks.

The numbers, before anything else

75%

returning rate

79%

solve rate today

53

top streak (days)

1,426,168

tiles flipped all time

A few of these I find more interesting than the headlines.

75% returning rate. Of everyone who has ever solved a Tessera, three in four have come back for at least one more day. That is the metric I care about for a daily-habit game. It is also the one that told me, around week three, that the thing actually had legs.

10% stickiness (DAU/MAU). For a free, no-account daily puzzle, that is a number I'm happy with. Above the rough 5% bar people use as "people who keep coming back unprompted." Not at the 20% NYT Games tier, but I am one person and they are not.

79% solve rate today, 66% all-time. Players who start a puzzle finish it most of the time. Difficulty calibration is roughly where I want it.

8% share rate. Roughly one in twelve solves gets shared. That share loop is doing most of the work of bringing new players in.

Three updates that made it self-driving

Three pieces shipped in the last few weeks, in this order. None of them are flashy. Together they're the difference between "a daily puzzle I have to publish each morning" and "a daily puzzle that publishes itself."

1. PostHog, on a diet

Tessera tracks gameplay through PostHog: every grid started, every swap, every solve, every share. The stats dashboard is built on the PostHog Query API, with a precompute cron that warms ClickHouse so the page loads fast.

The problem with default PostHog is that it tracks everything. Web vitals on every page. Pageleave on every navigation. Autocapture on every click. For a site with one interactive game and a stats page, that's mostly noise. Events that never make it into a query.

The diet, once I got round to it: cut web vitals, cut pageleave, kept gameplay events, kept share clicks, kept the daily-reminder event. Moved the stats subdomain off PostHog and Meta Pixel entirely, since nobody plays a puzzle there. Event volume dropped sharply. The dashboard didn't lose a single number it draws.

The story of how I "got round to it" is further down.

The default for every analytics tool is "track everything in case you need it later." The right default for a small product is "track the things you've actually queried in the last month."

2. Five social networks, one cron

The original social workflow was: solve the puzzle myself in the morning, screenshot the result, write a caption, post it to X. About ten minutes of fiddly phone work, every day, including weekends, including holidays.

The new workflow is a GitHub Action that runs at 08:00 UTC. It headlessly screenshots the day's puzzle, generates a caption from the stats precompute (today's grids started, solve rate, fastest solve), and posts to five accounts in parallel: X, Facebook (Page), Reddit (r/TesseraPuzzle), Bluesky, and Instagram.

Each platform has its own quirks. X needs OAuth 1.0a user context. Facebook needs a long-lived Page access token with pages_manage_posts. Reddit wants a "script" app and a real user password. Bluesky has its own app password system. Instagram was the most painful to provision but, once wired up, is fully automated like the rest. No manual upload step in the loop.

Each posting module is about 80 lines. The orchestrator is another 60. The thing that took longest was not the code. It was provisioning the credentials and reading five sets of API docs.

The piece I almost forgot: cache busting. The Open Graph image is generated at /api/og, and Facebook and X aggressively cache OG previews. The first day's post showed yesterday's puzzle. The fix was to append the date to the URL as a query string, which is enough to force a re-fetch.

3. Daily reminders, before anyone is awake

When a player solves the daily, the end-screen prompts them to opt into a reminder for tomorrow. The first version was simple: a Vercel cron at 09:00 UTC fanning out to every subscriber through the Loops transactional API.

It worked. Then it didn't. Loops rate-limits at a few hundred requests a minute, and the cron was firing them all in parallel. 429s started showing up in the logs. Some emails went out, some didn't, and I had no good way to tell which.

The fix took three rounds:

  1. Serialise the fan-out. No parallel requests. One subscriber at a time, with a small delay. Slower, but reliable.
  2. Retry 429s with backoff. Even serialised, occasional rate limits happen if Loops is busy. Exponential backoff, capped at three retries.
  3. Move the cron to 06:00 UTC. 09:00 UTC is mid-morning in the UK but mid-afternoon in Asia and overnight in the Americas. 06:00 UTC lands the email before people are scrolling their phones in the UK and Europe, which is where most of the playerbase is.

I also dropped the locale-suffix on the event name, so all reminders fire a single daily_reminder event in PostHog. That made the analytics line up cleanly with the rest of the funnel.

The thing that nearly broke it: a bus to Andorra

The piece that I'd left least defended was the analytics bill. PostHog's free tier is generous until it isn't, and the original tracking was happily firing on every page view, every navigation, every web vital, every autocaptured click.

I found out on a bus winding up into the mountains, somewhere between Spain and Andorra, that Tessera had quietly taken off. The dashboard was up, the share count was climbing, and the projected PostHog bill for the month was heading somewhere uncomfortable.

So I did the on-the-fly fixes from a phone with one bar of signal, on a bus that kept losing it. Cut web vitals. Cut pageleave. Dropped the analytics mounts on the stats subdomain. Pushed, watched the next deploy land, watched the event count drop. The rest of the holiday I refreshed the projected-bill chart roughly twice a day until I trusted it.

It worked. But it was the wrong order. The PostHog diet should have been week one, not "from a bus, in a panic, on holiday." That experience is most of the reason rule one below exists.

What this replaces

Before all of this, my Tessera morning was:

  1. Open the puzzle on my phone, solve it.
  2. Screenshot the start state.
  3. Write a caption that mentioned yesterday's solve rate (which meant opening PostHog on a phone, badly).
  4. Post to X.
  5. Post to Reddit, usually with different copy because of subreddit rules.
  6. Forget Facebook entirely most days.
  7. Trigger the reminder email manually if the cron had silently failed.

About twenty minutes, every morning, holidays included. The cost wasn't really the twenty minutes. It was that the puzzle felt like a chore I owed people, rather than a thing I made because I wanted to.

After:

  • I open Tessera when I want to play it.
  • I check the stats page when I'm curious.
  • I read the dashboard maybe twice a week.
  • The machine ships.

The five rules I'd give myself if I were doing this again

  1. Cap the bills before the launch, not after. Analytics, email, image generation. Anything with a per-event price. Trim the defaults and set spend alerts on day one. Don't find out from a bus in the Pyrenees.
  2. One credential pain barrier per platform. Don't try to add three new networks in one weekend. Each one has its own auth flavour, and you'll mix them up. One platform per session, end to end, until the post lands.
  3. Cache-bust your OG previews. Append the date. Save yourself the embarrassment.
  4. Don't write clever copy. Fixed sentence structure, dynamic numbers. The day the copy gets too clever is the day it ships something weird to five places at once.
  5. Cut the analytics events you don't query. Track gameplay. Don't track everything. The cost of "in case I need it later" compounds fast on a free tier.
Today's Tessera puzzle, the same image that gets posted to five social networks every morning.
The daily share card, posted to X, Facebook, Reddit, Bluesky, and Instagram at 08:00 UTC.

What's next

The machine is stable, which means I get to focus on the actual game again. A few things I'm thinking about, in no particular order:

  • Maybe a native app. The web build is doing the job, but a proper iOS and Android wrapper would unlock notifications, home-screen presence, and a smoother feel on the device most players are on.
  • Maybe a leaderboard. A daily one, anonymous-by-default, scoped to solve quality (tier and move count) rather than raw speed. The trick is doing it without spoiling the social bit, where the point is your own grid, not someone else's score.
  • Better feedback when a row or column locks in. Right now solving a single line is silent. The dopamine all arrives at the end. I want a small flourish the moment a row or column resolves: a colour change, a satisfying motion, something that says "you got that bit." Daily puzzles live or die on those little hits.

But the daily machine, I am no longer touching. It ran for a week without me. It can run for a year.

Try it: tesserapuzzle.com.