The problem, not the tarot
We didn't set out to build a tarot engine. We set out to make AI and Google do a hard job, unattended, every day.
The real brief was an engineering one: could we get AI and the Google ecosystem to solve a chain of separate problems — and tie them together so the whole thing runs itself, the same way, every day, for pennies?
We needed a demonstrator whose output a non-technical person could understand instantly — no manual, no jargon. A daily tarot reading fits: everyone recognises the format, and you can tell at a glance whether the result is good. So the tarot is the visible proof. The work is the pipeline behind it.
Fair, unbiased selection
A mechanical random draw in Apps Script — chance deals the cards, not the AI.
Data → real artefacts
A drawn number maps to a card identity and its hosted image, every time.
Consistent, on-voice AI output
Governed multi-persona prompts with a fixed editorial stance, so the voice never drifts.
Timing across async steps
A settle-guard that waits for the spreadsheet to finish recalculating before trusting it.
No two days alike
A title engine that remembers recent titles and refuses to repeat itself.
One reading, two channels
The same reading packaged as an HTML email and a live Blogger post.
Runs itself, near-zero cost
A single daily trigger on Apps Script and Sheets, plus shared hosting. No server.
None of these is hard alone. The achievement is making them work together — repeatable, programmatic, and consistent — with no one at the wheel.
How it runs, end to end
One daily trigger, seven steps, no human
Once a day, a single time-driven trigger wakes the script and it runs the whole sequence by itself. Nobody presses a button; nobody proofreads. The diagram below traces the real order the code runs in.
The daily run, start to finish — authored from the live executeDaily() sequence.
Reading the flow in plain language:
- Draw the cards. The script writes five random numbers into a "Randomizer" sheet.
- Wait for the sheet. A twenty-second pause lets the spreadsheet's own formulas turn those numbers into cards, per-card interpretations, and the holistic five-card insight.
- Write a fresh title. A title is generated that deliberately avoids the recent ones, so no two days read the same.
- Publish the reading. The finished reading is posted live to the blog through the Blogger API. If publishing fails, the run stops here — it throws rather than pretending it worked.
- Email a copy. A copy is emailed to us. Because this step only runs after a successful publish, that email doubles as the day's "it went out" signal.
- Remember the title. The published title is logged so tomorrow's title engine can steer clear of it.
The whole thing costs pennies to run: Apps Script and Sheets do the work, shared hosting serves the images, and there is no server to keep alive. The ordering is deliberately self-healing — the email only sends if the post actually published, so a silent failure can't look like a success.
How each problem got solved
The engine, piece by piece
Each problem from the first tab, solved concretely. This is the actual method, not a sketch of one.
The draw — fair selection
The script draws five numbers in the range 1–156 (the 78 Rider-Waite cards, each either upright or reversed). It enforces a minimum distance of five between draws and rejects duplicates, so a spread never clusters or repeats a card. This is plain randomness in Apps Script — the AI never picks the cards; chance does.
Number → artefact
Each drawn number maps, through the spreadsheet, to a specific card identity and its hosted image in a digital asset library. A bare integer becomes a real, presentable card.
The live hosted artefacts, served straight from the digital asset library — each one mapped from a drawn number. A reversed draw (like The Devil here) serves the upside-down variant.
The AI voices — governed output
Three separate personas write the words, each with a tightly governed prompt and a fixed editorial stance:
- Per-card voice — interprets each drawn card, in a persona schooled on Rider-Waite tradition, handling upright and reversed meanings.
- Holistic voice — reads the five cards together as one holistic insight across the Celtic-Cross-style formation.
- Zen voice — adds a short reflective "thought" drawn from Taoist and Confucian sources.
The reading bodies are written by claude-haiku-4-5. Every prompt states plainly that the reading is for insightful thought, reflection and motivation — not divination or fortune-telling.
The title engine — no two days alike
Titles run on claude-sonnet-5. The engine is fed the most recent titles to avoid, then scores and regenerates candidates until the word-overlap with recent titles drops below a set threshold. The result: a fresh headline every day, never a near-repeat of last week's.
The timing guard
The holistic insight is calculated by the spreadsheet asynchronously, so it isn't ready the instant the numbers land. The title routine waits until that value looks settled — not empty, not still "Loading…" — before it trusts it. It's a small guard that stops the whole run tripping over its own timing.
Two channels, one reading
The same reading goes out two ways: an HTML email to us, and a live post to the blog via the Blogger API. The post is built with its own class hooks while the layout lives in the blog theme — and the theme's HTML and CSS were themselves AI-developed.
See it live
A new reading every day
Every day, without anyone touching it, a complete reading lands on the blog: a fresh title, a five-card spread in a Celtic-Cross-style formation with a per-card interpretation, a holistic insight across all five, and a short Zen thought to close.
It is deliberately cheap and deliberately simple — a single daily trigger, a spreadsheet, some governed AI, and shared hosting. That it runs itself, every day, for pennies, is the boast, not a caveat. The human sets the taste and the guardrails; the AI does the daily graft; and chance, not the AI, deals the cards.