You Might Also Like
Blackjack vs Dealer — Free HTML CSS JS Snippet
Blackjack Card Game vs Dealer · Games · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Blackjack Card Game — Soft/Hard Ace Scoring, Automatic Dealer AI & Fisher-Yates Shuffle

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:
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
- 1Deal 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.
- 2Hit 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.
- 3Watch 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.
- 4Stand 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.
- 5Watch 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.
- 6See 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
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.