Skip to content
patience

solitaire · pixel cards · webassembly

Deal me in, like it's 1990.

patience is Klondike, FreeCell, and Spider in your browser, on a chunky retro deck drawn pixel by pixel in code — 71×96, the exact card size Windows 3.0 shipped. Unlimited undo, safe auto-complete, and when you win, the cards bounce. Written entirely in Go.

patience — Klondike · draw 1
FOUNDATIONS  A♠ 2♠  A♥  A♦ 2♦ 3♦  ·

TABLEAU
K♠ Q♥ J♣ 10♦ 9♠   ← a run in progress
7♥ 6♣ 5♦            ← drag the whole stack

moves 41 · u undo · a auto-finish · safe sends only ✓

what you get

Three families of patience, one deck.

Klondike, both ways

The classic — draw 1 for a gentle game, draw 3 for the proper fight, with unlimited passes through the stock.

FreeCell with supermoves

All 52 cards face-up from the deal. The engine computes true supermove capacity — including the halving when you land on an empty column.

Spider, 1 · 2 · 4 suits

Two decks, ten columns, and complete king-to-ace runs that lift clean off the table — at whichever suit count your patience allows.

Unlimited undo

Every move snapshots the whole table, so undo walks all the way back to the deal — through stock recycles, flips, and Spider run removals.

Safe auto-complete

Double-tap sends a card home; one press of A finishes a won game. Auto-sends follow the classic safe rule, so it never strands a card you still need.

The win you remember

Cards launch from the foundations, bounce along the bottom, and smear trails across the screen — the 1990 victory lap, faithfully recreated.

under the hood

A rules engine you can test in the dark.

Each variant is a small implementation of one Rules interface over a shared table of piles — the whole rules core has zero rendering imports, so deals, move legality, supermove capacity, Spider's run-removal chains, and full undo round-trips are verified headlessly in CI against seeded golden deals. A card is one byte.

// a card is one byte
type Card uint8 // rank·suit·face-up

g := solitaire.New(Klondike(3), seed)
g.TapStock()
err := g.Apply(Move{Src: 6, Idx: 4, Dst: 8})
g.Undo() // full snapshot, always legal
# keys
drag  move cards   tap stock  deal
double-tap  send to foundation
u  undo   h  hint   a  auto-finish
n  new deal   r  restart seed   esc  menu