Skip to content
Work in progress. These docs describe Spawnite at launch, and some parts are still being built.

Crazy Eights

Crazy Eights is a card game for one player against one to three bots, at a card table drawn in 3D. It plays alone, with no room for friends.

The following commands write a copy of Crazy Eights, install it and play it:

Terminal window
npx @spawnite/cli create my-crazy-eights --template crazy-eights
cd my-crazy-eights
pnpm install
pnpm dev

The start screen takes the number of bots, and Play deals a round: seven cards each for two at the table, five each for more. One card is turned to start the discard pile; an eight turned up goes back under the deck.

On a turn, a participant does one of the following:

  • Plays a card that matches the top card’s suit or its rank.
  • Plays an eight, which is wild, and names the suit the next card must match.
  • Draws one card from the deck, only when the hand holds no card to play. The turn then passes.

When the deck runs out, the discard pile under its top card is shuffled into a new deck. When no deck can be made, a participant with no card to play passes, and the round ends once everyone passes in a row. The first empty hand wins; the rest place by the cards they hold, fewest first.

A turn lasts 30 seconds. When the player’s clock runs out, the game plays for the player the move a bot would choose: a card that matches, else an eight, else a draw. A bot takes 1.6 seconds over its move. The clock and the bots wait while the round is dealt.

The cards the player may play stand up with a white edge and an arrow; the rest stay down and dim. A tap or a click selects a card and a second one plays it; a drag onto the discard pile plays it too, and a drag released anywhere else puts the card back. A card that cannot be played says why. An eight lays four cards between the piles and the hand, one for each suit, and a tap picks the suit.

With no card to play, the player draws: a tap on the deck, or a drag of its top card off the deck. Let go over the deck, the card goes back.

The table is a 3D poker table in a dark room: blue felt under a lamp, a gloss mahogany racetrack and a padded black armrest, after the tables BBO and Darafeev build. ?table=tavern in the address lays a flat wooden tavern table in its place. On an upright phone the camera stands back to show part of the rail: phoneRim in src/scenes/Table.tsx, from 0 (the cards alone) to 1 (the whole rim), is 0.8, and calculateFramedSize in the card kit turns it into the size the camera frames. When a round ends, won or lost, TableCamera’s shown size makes the camera tilt up and come in on the table while the result fades in; cardMotion.showTable holds its seconds, pitch and zoom. Before the first round the camera stands back and up, and TableCamera’s seated sits it down over the deal; cardMotion.sitDown holds its seconds, pitch and distance. Your cards are dealt flat onto the felt in front of you and rise into your hand, over the near rail, once the deal ends. After an eight, the ace of the suit it named drops onto it and stays until the next card covers it. On the development page, Ctrl+E opens the devtools: the Round panel’s Win and Lose end the round with you first or a bot first, and Eight lays an eight on the pile naming another suit.

The top of the screen holds only the countdown, which turns red for the last ten seconds. A bot’s last move shows under its name.

The rules are plain data in one trait and functions over it, in src/rules/rules.ts, and one system steps the clock and the bots. The shuffle is the engine’s shuffle, which draws from the world’s seeded random source, so a seed deals the same round every time.

The game keeps two kits under src/kits/, source a second card game would copy: the cards model with its piles, deal and moves, and the turn model with its order and clock. The card view beside them draws one mesh for each card from the atlases in packages/assets/textures/cards/ and moves it with the motion package, with the timings of the motion design in motion.json there.