The problem
I wanted to make something visual that began as my own idea and was carried the rest of the way with AI: not a model’s default picture of tarot, but the deck I had in mind, down to its palette. Tarot suited that, because it is a complete visual language of 78 images, and every reading is a small piece of writing about them.
The idea draws on the Victorian language of flowers, floriography, in which a bouquet could say what people would not say aloud. Here every card is a still life of flowers in place of the traditional figures, and the reading is what they have to say.
Two things stood in the way of the site I wanted:
- A deck is 78 pictures that have to look like one deck. Generated one at a time, images drift in palette, framing and detail.
- A language model knows the traditional deck too well. Asked about the Hermit, it describes an old man with a lantern, whatever is actually on the card in front of you. My deck has no people in it at all.
Why it matters
A reading is read as something said to you personally. A sentence that sounds like a verdict (“this is destined”), a guess at a question you didn’t say out loud, or a confident line about health or money lands harder than the same sentence in a chatbot. So the readings had to describe the cards on screen, follow the way the question was asked, and stay on the side of possibilities rather than predictions.
Approach
01 · Ask
Keep the question silent, name a theme, or write it down.
02 · Draw
78 petals fall; each one is a card, shuffled with a cryptographic RNG.
03 · Read
Claude gets each position, card, orientation, keywords and this deck’s picture.
04 · Eval
Rule checks and an LLM judge grade the readings on 30 fixed cases.
- Give the model the deck, not just the card name. Every card has a written description of its picture in this deck, and the request carries it alongside the traditional keywords. The prompt says to describe only that picture.
- Fit the reading to how you asked. A silent question gets an open reading that leaves room (“if your question is about a relationship…”); a theme keeps to the theme; a written question gets answered directly.
- Make the deck in parts. Illustrations, the frame and the card names are made separately and put together by code, so every card shares exactly one frame and one typeface.
What I built
- The ritual: ask, breathe along with a ring of light (skippable), then pick petals as they settle. A picked petal floats to the centre, becomes a card and flies into its slot. Cards turn over one by one, and the reading streams in, with up to six follow-up questions.
- Five spreads: a single card; past, present and future; situation, obstacle and advice; two paths; and the Celtic Cross.
- The deck: 78 Art Nouveau floral still lifes in a pale rose and sage palette, with no people, faces or hands. A picker page let me choose each card from its generated versions.
- Two languages: the interface, the 78 cards’ keywords, the spreads and the reading itself switch between Chinese and English, mid-reading if you like, without reshuffling the cards.
- Keepsakes: readings are saved in the browser, and any reading can be saved as a 1080×1350 image.
- A public demo: a static build with no server and no API key. Each spread deals a preset set of cards and plays back a reading Claude wrote for them in advance, and the page says so.

The draw: every petal is one of the 78 cards. Picked petals turn into cards and fill the slots below.

The 22 major arcana in the deck viewer (labelled in Chinese). One style prompt, twelve rotating background motifs and a single frame keep 78 generated pictures looking like one deck.
Key decisions
- No text and no frame in the generated art. The image model paints full-bleed illustrations; the frame is generated once, and the code finds the frame’s cream window by flood fill, sets the illustration behind it and writes the card’s name on the plaque.
- Twelve background motifs, assigned in turn. So that 78 cards from one style prompt don’t all look alike, each gets the next of sun rays, lace, ribbons, stained glass and the rest as its ground.
- A separate English prompt, not a translated reading. The English reader has its own headings and its length in words rather than characters, so neither language reads as a translation.
- Recorded readings for the demo. A public page that called the API would spend money on every visit and need a key on a server; a demo with preset cards and real recorded readings shows the whole experience for free, and is labelled as a demo.
- Kept Opus 5.5 after the eval. The eval couldn’t separate Opus from Sonnet 5.5 (−3 points, ±5), and Sonnet costs 40% as much. But Opus missed once in 60 and Sonnet three times, and Sonnet’s misses were the kind that matter most here: an outcome stated as certain, and circumstances invented for an open question. On a personal site the difference is about two cents a reading. If the site ever served many readers, the eval says Sonnet is the first thing to try.
- Kept the prompt’s length limits, and measured against a readable range instead. Readings run about 20% over the stated lengths (a single card in Chinese: 456 characters against 300). Raising the numbers would likely push readings longer still, so the prompt keeps them as a brake and the eval checks a range that reads comfortably.
Results
The eval. 30 fictional cases: 15 in Chinese and 15 in English, across the five spreads and the three ways of asking, with four questions about health, a legal dispute or money that should be referred to a professional. Cards were drawn once with a fixed seed, so every model reads the same cards. Each case ran twice per model. Rule checks cover structure, a header for every card with the right orientation, language, the referral and length; Claude Fable 5.1, which was not one of the models being compared, judged five properties: describing only this deck’s pictures, fitting the way the question was asked, no fatalism, the cards’ meanings, and concrete advice. Before the full run the rules were checked on blank, wrong-language and wrong-orientation readings, and the judge on a blank one, “I don’t know” and an answer to a different question; I reviewed six graded readings before signing off on the grading.
| Opus 5.5 | Sonnet 5.5 | Haiku 5.5 | |
|---|---|---|---|
| Passed every check | 98% ±3 | 95% ±5 | 88% ±9 |
| Cost per reading | $0.033 | $0.013 | $0.0009 |
| Median wait | 17 s | 11 s | 10 s |
| Length within range | 60/60 | 53/60 | 59/60 |
- Opus: one miss in 60. A Celtic Cross reading in Chinese said part of the road was “destined” (注定) to be walked alone.
- Sonnet: −3 points against Opus on the same cases (95% interval ±5), so the eval can’t tell them apart. Its misses were one outcome stated as certain, five suggestions where the prompt asks for two to four, and details invented for an open question. Its English single-card readings ran long (340–380 words).
- Haiku: −10 points (±10), with real content errors: twice it described figures from the traditional deck (the Hermit as a man carrying a lamp), once it left out the advice section, and once it asked what the two paths stood for instead of reading them. Three more misses were headers that shortened the position names.

A reading in English. Each card is introduced by its position, name and orientation, then described as it appears in this deck.
Limitations
- The eval is small and synthetic: 30 cases at two runs each resolve differences of roughly ten points, not three.
- One judge model grades the content criteria; beyond the six readings I reviewed, it hasn’t been checked against human labels.
- The eval covers the first reading only, not follow-up questions.
- Reading history lives in one browser’s storage, and the full site runs on my own computer.
- The demo can’t take a question or a follow-up; its readings were recorded in advance.
What I learned
- A number in a prompt is an instruction, not a measurement. The readings settled about 20% above the stated lengths; measuring them showed where the spec was simply too tight for the structure it asked for.
- Ground the model in what’s on screen. Without each card’s description, “the Hermit” means the traditional old man; with it, the stronger models described this deck every time, and the eval caught the one that didn’t.
- An eval turns a model choice into a visible trade. Quality, cost and wait time side by side made the decision about Opus and Sonnet an explicit one.
Tech stack
- Node.js server with no framework; plain JavaScript front end with no build step; marked and DOMPurify for the streamed Markdown.
- Claude API: Opus 5.5 at medium effort, streamed, with server-side fallback on refusals.
- Gemini (Nano Banana) for the art; sharp for compositing the cards and cutting out the petals.
- Evals: a Node runner with Claude Fable 5.1 as judge, using structured outputs.
- Developed with Claude Code.
