Personal project · 3 days · Solo · Live
The only superstition that compiles.
A daily tarot for programmers, in Oscar Wilde's voice and Beardsley's line.
Coding looks rational from the outside. It compiles or it doesn't. But anyone who has written code long enough knows about the bug that vanishes in the debugger, or the deploy that finally works at 3am.
I made Codices to take that quiet superstition seriously: 22 Major Arcana reframed as programmer archetypes. One card per day. Three days from illustration to deployment.
A quiet superstition
Programmers don't talk about the magic creeping in. We pretend the machine is logical because we have to. I dressed the product in Oscar Wilde's voice because he understood that beauty can be serious without becoming solemn.
A tarot deck for programmers, in Wilde's voice, illustrated like a Beardsley codex. Rational systems, aesthetic rituals, and older superstitions all sit inside the same page.
IXthe aesthete
beardsley
IIthe voice
beardsley
VIIthe line
beardsleyThe product
Codices reframes the 22 Major Arcana as archetypes of a programmer's life. The Fool becomes // hello world. The Tower becomes // the production incident. Death becomes // the refactor.
Three Example Cards
Flip the card. Let the codex answer back.
One card per day. No re-draws. The card you receive was always going to be yours. The constraint wasn't a flex. It was the only way to keep it from becoming a real thing.
Click the card to flip
Three questions I started with
What does tarot look like if it knew git?
Traditional tarot often speaks in vague poetry. I wanted cards that spoke in the dialect of the people reading them. The Tower did not need to be redrawn. It just needed to become // the production incident.
What if every card sounded like Oscar Wilde wrote it after a long sprint?
Voice was the hardest design decision. A horoscope felt too vague, self-help felt too earnest, and Stack Overflow felt too cold. Wilde gave me a sharper rule: treat small things seriously and serious things lightly.
What if a product could be useful by being useless?
Codices doesn't predict anything. It doesn't optimize your workflow. It just makes you pause and read a sentence for 90 seconds. In a feed designed to never let you go, a product that demands you stop and then dismisses you is, accidentally, a luxury.
Design system
Visual language
Hairline white linework on pure black. Gothic arch composition. Hieratic flat hands holding sacred objects. Strict symmetry, medieval manuscripts, and terminal output as poetry.
Typography
Cormorant Garamond, weight 300, italic for narrative. JetBrains Mono, lowercase, for code-style headings. Two fonts, infinite hierarchy.
Color
- Black
#0A0A0A - White
#EDEDED - Gold
#C9A961 - Incident
#C9302C
Five colors. Used like punctuation.
The ceremony
Four screens. Each one designed to slow you down.
- 01
Arrival
A landing page whose subtitle changes by hour. At 11pm: "the night is a long await." At 3am: "the void is listening." You enter a birth timestamp, not a name or email.
- 02
The compilation
A 4.5-second loading sequence that cannot be skipped. A terminal log initializes the runtime, loads 22 archetypes, and calculates fate.signature. The ritual is the product.
- 03
The reveal
Your card enters in eight phases over three seconds: border, roman numeral, traditional name, illustration, title, subtitle. The first time you see your card is the only time it animates.
- 04
The reading
Scroll and the card docks to the corner. The reading unfolds as narrative, do and don't lists, lucky metadata, and a companion card. Every card follows the same structure.
async function drawCardForUser(birthTimestamp) { const today = new Date().toISOString().split('T')[0]; const julianDate = julianFromMs(Date.now()); const moon = await fetchMoonPhase(); const moonSeed = Math.floor(moon.Illumination * 100); const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; const seedString = [birthTimestamp, today, julianDate, moonSeed, timezone].join('|'); const hashHex = await sha256Hex(seedString); return parseInt(hashHex.slice(0, 8), 16) % 22;}
The writing
Every reading follows six small moves:
- 01Start with the card's emotional center. For The Tower, that means sudden collapse.
- 02Move it into a programmer's day. The Slack channel fills with red alerts at 10pm.
- 03Open with a Wilde-shaped observation. The first sentence has to earn the reader's trust.
- 04Make the advice concrete, but keep it strange enough to feel remembered.
- 05Use metadata as worldbuilding. A lucky color becomes the green of a successful deploy checkmark.
- 06End with a companion card. The pairing should feel true before it is explained.
“You will fix it. You always fix it. This is your job, and this is your tragedy.”
Sample opening · The Tower
The invisible layer
Eight hidden discoveries sit quietly in localStorage. Each one triggers a brief banner. In the About modal, a vine grows one leaf per discovery until a flower blooms at the top.
- Visiting between midnight and 4am (the gilded hours)
- Drawing a card on your birthday
- Drawing during an angel number (3:33, 11:11, 22:22)
- Drawing on Christmas, NYE, or Lunar New Year
- Drawing Friday after 6pm, when the week has finally compiled
- Drawing Sunday after 10pm, when Monday is already approaching
- Drawing during a full or new moon
- Your first visit
Most users will see two or three. A few will see all eight. The point is the slow realization that the product has been quietly watching you back.
Under the hood
A single HTML file, about 4,400 lines. No dependencies beyond React, html-to-image, and qrcode-generator. One file, one person, no room for a build pipeline to break.
| Layer | Choice | Why |
|---|---|---|
| Runtime | React (in-browser) | Ship a personal artifact in three days without a build pipeline to break |
| Illustration | Custom SVG in Figma | One codex with the same arch, line weight, and object vocabulary on every card |
| Draw | Deterministic seed | birth timestamp + date + moon phase, one card per day, always yours |
| Memory | localStorage | Silent discovery tracking without accounts |
| Share | html-to-image + QR | Share card that looks like Codices, not a screenshot |
What I learned
Designing for ceremony is harder than designing for speed.
Every skip button I considered got removed. The 4.5-second compilation is part of the reading. It works because the product is not trying to move you somewhere else. It is trying to keep you here briefly.
Voice is a constraint, not a decoration.
Limiting myself to Wilde-shaped sentences forced clarity I wouldn't have found otherwise. I couldn't hide behind motivational vagueness. Every line had to be specific, observed, and earned.
A product can be useful by being useless.
Codices doesn't predict, track, or notify. It makes you stop for 90 seconds and read a sentence about your own life. That uselessness is the value.
Constraints make worlds.
Two fonts. Five colors. One composition rule. One card a day. Three days to ship. I started with limits, and the world built itself inside them.
What I'm trying to do
The best products often hold two things at once. For Codices, that meant the logic of code and the decadence of tarot. Git and superstition. Stack Overflow and Oscar Wilde.
I like products that come from that kind of friction. They feel specific instead of universal, beautiful without apology, and useful in ways that are not always obvious at first glance.
