A card-drawing interface is a deceptively nasty frontend problem. The interaction is trivial — tap, reveal one image — but the asset set is large, the art is the entire product, and the user is on a phone. Ship it naively and you have a 40 MB page showing a gray rectangle at the exact moment the experience is supposed to land.

Rule one: never ship the deck, ship the card

The instinct with a 78-card tarot set is to preload it so reveals are instant. Do the arithmetic first. Even at a disciplined 80 KB per card, 78 cards is 6 MB — before the back face, frame art, and whatever texture overlay makes it look good. On a mid-tier phone over cellular you've just spent the user's entire patience budget on 77 images they'll never see today.

The correct model is a one-card-plus-back critical path. Before first paint you need the card back (shown immediately, often animated), the frame chrome (ideally CSS or inline SVG rather than a raster), and the single card image for today's draw. Everything else is lazy. This turns an O(deck) problem into an O(1) problem, and every optimization after it is a rounding error by comparison.

The routing consequence: your draw must resolve early enough to start the image fetch during the round trip that serves the HTML. If the card is chosen client-side after hydration, you've serialized document → JS → decision → image fetch, and that chain is what you feel as slowness. Resolve server-side, then preload in the head: