Blackjack vs Dealer — Free HTML CSS JS Snippet

Blackjack Card Game vs Dealer · Games · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

handValue() dynamically re-evaluates soft/hard Ace scoring on every card drawn, not just once at deal time
Fisher-Yates shuffle for an unbiased fresh 52-card deck built at the start of every round
Automatic deck reshuffling via drawCard() if the deck is ever exhausted mid-round
Face-down dealer hole card rendered with a distinct diagonal-stripe CSS pattern until the round resolves
Automatic dealer AI following the fixed "stand on 17" house rule, drawing with a readable setTimeout pace
Immediate-loss bust detection that skips dealer play entirely, matching real Blackjack rules
Natural Blackjack (21 on the initial two cards) detected and resolved instantly on deal
Persistent session-long win/loss/push tally, incremented by resolveRound() and displayed live

About this UI Snippet

Blackjack Card Game — Soft/Hard Ace Scoring, Automatic Dealer AI & Fisher-Yates Shuffle

Screenshot of the Blackjack Card Game vs Dealer snippet rendered live

Blackjack is deceptively simple to describe but genuinely tricky to implement correctly, because a single rule — how an Ace is scored — has to be recalculated dynamically every time a card is drawn, not decided once. This snippet builds a complete, playable single-player Blackjack round against an automatic dealer, using nothing but vanilla JavaScript for the rules engine and CSS for card rendering, with every core rule implemented rather than approximated.

Soft and hard hand scoring

The trickiest part of Blackjack logic is that an Ace is worth 11 by default but must demote to 1 whenever counting it as 11 would bust the hand — and this has to be re-evaluated every time a new card is drawn, since a hand that was "hard" (no usable Ace) can become "soft" again as cards are added, or vice versa. The handValue() function handles this cleanly: it first sums every card assuming every Ace is worth 11 while counting how many Aces are present, then runs a loop that subtracts 10 (demoting one Ace from 11 to 1) for as long as the total exceeds 21 and at least one Ace is still counted as 11. This produces the correct best-possible total in every situation — a hand of Ace-6 correctly reads as a soft 17, and if a third card like a 10 is drawn, the same function correctly re-evaluates it as a hard 17 without any special-case branching.

The dealer's fixed, rule-bound AI

Once the player stands, the dealer's hole card (drawn face-down at deal time and rendered with a diagonal-stripe pattern) is flipped face-up, and dealerPlay() runs an automatic loop: the dealer draws another card any time its total is below 17, and stops the instant its total reaches 17 or higher — this is the standard, unmodifiable house rule used at real casino tables, sometimes called "dealer stands on all 17s." Each dealer draw is spaced out with a short setTimeout so the player can actually watch the hand build up rather than seeing it resolve instantly.

Deck management and shuffling

A fresh 52-card deck is built and shuffled with the Fisher-Yates algorithm — the standard unbiased in-place shuffle, iterating from the last card to the first and swapping each with a randomly chosen earlier-or-equal-index card — at the start of every round, which is simpler and just as fair as tracking a persistent shoe across rounds for a casual single-player demo. drawCard() pops from the end of the shuffled array and silently rebuilds and reshuffles a new deck if it is ever exhausted, so the game can never run out of cards mid-round.

Round resolution and the session tally

Busting (exceeding 21) ends the round immediately as a loss without waiting for the dealer to play, matching real Blackjack rules where a bust is an instant loss regardless of what the dealer holds. A two-card total of exactly 21 on the deal is recognised as a natural Blackjack and resolves the round immediately. Standing triggers the dealer's automatic play, and finishRound() compares final totals: a dealer bust is an automatic win, a strictly higher dealer total wins for the dealer, a strictly higher player total wins for the player, and equal totals resolve as a push (tie), crediting a running win/loss/push tally that persists for the whole session across a "New Round" button.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to trace through handValue() with a specific hand like Ace, Ace, 9 to confirm it correctly resolves to a soft 21 rather than double-counting both Aces as 11 — working through an edge case like that by hand alongside the AI is the fastest way to trust the scoring logic. It's also a good snippet to extend with AI help: ask it to add a simple chip-based betting system that adjusts a bankroll based on resolveRound()'s outcome, implement splitting when your first two cards are a matching pair, or add a "hit on soft 17" dealer variant that requires tracking hand softness rather than just the numeric total. You could also ask the assistant to review the Fisher-Yates shuffle implementation for correctness against the textbook algorithm.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a single-player Blackjack card game against an automatic dealer in plain HTML, CSS, and JavaScript — no frameworks, no libraries, no card image assets (render cards as styled rectangles with rank and suit text).

Requirements:
- A "Deal" button shuffles a fresh 52-card deck using an unbiased shuffle algorithm and deals two cards each to the player and dealer, with the dealer's second card shown face-down until the round resolves.
- Implement correct Ace scoring: an Ace counts as 11 unless that would cause the hand to bust, in which case it counts as 1, and this must be recalculated correctly as more cards are drawn (a hand can move between "soft" and "hard" states across multiple draws).
- A "Hit" button draws one card into the player's hand; if the resulting total exceeds 21, the round ends immediately as a loss without revealing the dealer's hidden card or letting the dealer draw.
- A "Stand" button reveals the dealer's hidden card and then has the dealer draw automatically, one card at a time, until the dealer's total is 17 or higher, following the standard house rule.
- After the dealer finishes, compare final hand totals to determine a win, loss, or push (tie), accounting for a dealer bust as an automatic player win.
- Track and display a running win/loss/push tally across the whole session, and provide a "New Round" button that clears the table for another deal without resetting the tally.
- Detect a natural Blackjack (a two-card total of 21 immediately after dealing) and resolve it right away rather than allowing further hits.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Deal a new roundClick "Deal" to shuffle a fresh deck (buildShuffledDeck() with a Fisher-Yates shuffle) and deal two cards each to you and the dealer. The dealer's second card is dealt face-down, rendered with a striped pattern.
  2. 2
    Hit to draw another cardClick "Hit" to draw one more card into your hand. handValue() recalculates your total immediately, correctly re-evaluating any Ace as 1 or 11 depending on what keeps your hand at or under 21.
  3. 3
    Watch for a bustIf your total exceeds 21 after a Hit, the round ends immediately as a loss — busting is an instant loss regardless of what the dealer is holding, exactly like real Blackjack rules.
  4. 4
    Stand to end your turnClick "Stand" once you are satisfied with your total. The dealer's hidden card flips face-up and dealerPlay() begins drawing automatically.
  5. 5
    Watch the dealer play by the house ruleThe dealer draws cards one at a time, with a short pause between each, until its total reaches 17 or higher — a fixed rule with no strategic choice, exactly matching real casino dealer behaviour.
  6. 6
    See the result and track your tallyfinishRound() compares final totals to declare a win, loss, or push, updating a running session tally. Click "New Round" to clear the table and deal again.

Real-world uses

Common Use Cases

Teaching state machines and conditional recalculation logic
The Ace soft/hard scoring rule is a genuinely good teaching example of why some values cannot be computed once and cached — they must be recalculated from scratch whenever the underlying data (the hand) changes. Combined with the deal/hit/stand round state machine, this snippet is a compact, realistic example of both patterns for students studying game or application state management.
Portfolio piece demonstrating card-game rules engines
A correctly implemented Blackjack — with proper Ace logic, dealer AI bound to house rules, and accurate win/loss/push resolution — is a recognisable way to demonstrate rules-engine thinking and edge-case handling in a portfolio or take-home coding exercise, well beyond what a static card-layout mockup can show.
Casual replayable diversion embedded in a site
A self-contained, dependency-free Blackjack round is an easy drop-in for a "break room" or "just for fun" section of a personal site, internal tool, or waiting screen, giving visitors a genuinely playable card game with no server or backend required.
Card table UI and animated card-deal pattern reference
The green felt table background, simple styled-rectangle card rendering (rank plus suit symbol rather than illustrated art), and the CSS keyframe deal-in animation on each new card are a reusable visual reference for any card-based UI — memory-match games, flashcards, or hand-of-cards displays.
Base for betting, splitting, or doubling-down variants
Because the round lifecycle (deal, hit, stand, resolve, new round) is cleanly separated into named functions, this snippet is a practical starting point for adding a chip-based betting system, a double-down action, or hand-splitting on a starting pair — each of which builds on the same handValue() and dealerPlay() foundation.
Probability and expected-value teaching tool
Because the shuffle and dealing logic are fully transparent and inspectable in the source, this snippet doubles as a sandbox for exploring basic Blackjack probability and strategy concepts — for example logging every round's outcome to compare observed win rate against known basic-strategy expected values.

Got questions?

Frequently Asked Questions

handValue() first assumes every Ace is worth 11 and sums the hand, while separately counting how many Aces are present. It then runs a loop that subtracts 10 from the total (equivalent to re-counting one Ace as 1 instead of 11) for as long as the total exceeds 21 and at least one Ace is still being counted as 11. This means the function always returns the highest possible total that does not bust, recalculated fresh every time it is called — so a hand's Ace value automatically adjusts as more cards are drawn.

The dealer follows "stand on all 17s": dealerPlay() draws another card any time the dealer's total is below 17 and stops the instant it reaches 17 or higher, matching the most common real-world casino rule. To implement "hit on soft 17" instead (a stricter variant), you would need to also track whether the dealer's 17 is soft (contains an Ace still counted as 11) and continue drawing in that specific case — handValue() would need a companion function that reports softness, not just the total.

In real Blackjack, if your hand exceeds 21, you lose immediately regardless of what the dealer is holding or would have drawn — the dealer never even needs to reveal their hole card or play out their hand. The hit() function checks handValue(playerHand) > 21 immediately after each draw and calls resolveRound() as an instant loss, skipping dealerPlay() entirely, which matches this real rule.

buildShuffledDeck() constructs all 52 rank/suit combinations and then applies the Fisher-Yates shuffle: iterating the array from the last index down to the second, and at each step swapping the current card with a card at a uniformly random index from 0 up to and including the current index. This is the standard algorithm for producing a mathematically unbiased random permutation, unlike naive approaches such as sorting by Math.random() which are not uniformly distributed.

No — this snippet focuses purely on the core round logic (deal, hit, stand, dealer AI, win/loss/push resolution) and a running tally of round outcomes, without a betting or bankroll system. To add one, introduce a chip balance variable, a bet-amount input or chip-selection UI before each Deal, and adjust the balance inside resolveRound() based on the outcome and bet size — the existing win/loss/push branching is the natural hook point for that logic.