Blackjack vs Dealer with Chips and Hints — Free HTML CSS JS
Blackjack Card Game vs Dealer · Games · Plain HTML, CSS & JS · Live preview
For supporters
Source Code
Want the source code?
Play the game for free. Become a WebDevPuneet supporter to unlock the complete HTML, CSS and JavaScript source code, the exports (HTML, React, Vue, Angular, Tailwind) and Fork.
Extend with AI
Take This Snippet Further With AI
The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.
What's included
Features
About this UI Snippet
Blackjack Card Game — A Casino Table With Chips, Double Down, 3:2 Payouts and a Strategy Hint

Blackjack looks simple but rewards getting the details right: an Ace that changes value, a dealer who follows a fixed rule, a blackjack that pays more than an ordinary win, and a tie that returns your bet. This snippet is a complete single-player table in plain JavaScript and CSS, with every one of those rules implemented, real playing-card faces drawn without any images, chips and a bankroll, and the extras that make it feel like a game rather than a demo: a card that flies from the shoe, a hole card that flips in 3D, and a hint button that explains what basic strategy would do.
Soft and hard totals
score(hand) adds every card with each Ace counted as 11, remembers how many Aces there are, and then demotes them to 1 one at a time while the total is over 21. It returns both the best total and whether the hand is "soft" (an Ace is still counting as 11). A hand of Ace and 6 reads as a soft 17, and drawing a 10 turns it into a hard 17 without any special-case code, because the total is recalculated from scratch on every card.
A fixed dealer, and the real rules of the table
After you stand, the dealer's hole card flips and dealerTurn() draws until the total reaches 17, then stops, whether the 17 is hard or soft ("dealer stands on all 17s", printed on the felt). A natural is checked the way a casino checks it: when the dealer shows an Ace or a ten-value card, the hole card is peeked at straight away. Player blackjack against a dealer without one pays 3 to 2; both having blackjack is a push, which the earlier version of this snippet scored as a win; a bust ends the round at once as a loss.
Chips, payouts and double down
Bets are built from chips of 10, 50, 100 and 500, and the bet is shown as a stack drawn greedily from the highest chip down. finish() is the one place money moves: a win returns the bet twice, a blackjack returns it 2.5 times, a push returns it once and a loss returns nothing, so the ledger always balances. Double down is allowed on the first two cards when you can afford it: the bet doubles, you get exactly one more card, and the dealer then plays. Chips and the win, loss and push record are saved to localStorage in a try/catch, and a two-press reset button starts over.
A six-deck shoe
The shoe holds six shuffled decks (312 cards), built with the Fisher-Yates shuffle, which is the standard unbiased in-place shuffle. draw() pops from the end, a new shoe is shuffled when fewer than 78 cards remain, and the shoe counter beside the stack shows how deep you are.
Cards drawn with CSS
Each card is built from elements: a corner index at the top left and, rotated 180 degrees, at the bottom right; pips for the numbers 2 to 10 placed from a table of column and row positions (the lower half drawn upside down like a real card); a large suit for the Ace; and a framed letter for the court cards. The back is a repeating-gradient pattern. The dealer's hole card has a front and a back with backface-visibility: hidden, and removing the down class rotates it 180 degrees on the Y axis.
Animating the deal with FLIP
When a card is dealt, addCard() measures the cards already in the hand, adds the new one (which makes the hand re-centre), and animates each old card from its previous position back to the new one with the Web Animations API. The new card flies in from the shoe's position the same way. Only transforms are animated, so the motion stays smooth, and visitors who prefer reduced motion get an instant deal.
A hint that explains itself
The Hint button runs advice(), a compact basic-strategy table for six decks with a dealer who stands on 17. It looks at your total, whether it is soft, the dealer's up card and whether doubling is still possible, then names the move and gives a one-sentence reason. The recommended button pulses gold. It never plays for you.
Keyboard and accessibility
H hits, S stands, D doubles (or deals when betting), and ? shows the hint. The message line is a live region, every card has an aria-label such as "Queen of hearts" (the hole card says "Face-down card" until it is revealed), and the hands announce their cards and total.
Step by step
How to Use
- 1Play the hand you are dealtA first-time visitor starts with a hand already dealt: a Queen and a 5 against a dealer 7. Press Hit or Stand. After that, every hand starts with your own bet.
- 2Place a betClick chips to build a bet of 10, 50, 100 or 500 each, up to your chips. Clear removes it, and Deal starts the round with a card flying from the shoe to each hand.
- 3Hit, stand or doubleHit takes a card, Stand ends your turn, and Double (on your first two cards) doubles the bet and gives you exactly one more card. Going over 21 is an instant loss.
- 4Ask for a hintPress Hint (or ?) and the table names the basic-strategy move with a short reason. The matching button pulses. Try it on a soft hand such as Ace and 6.
- 5Watch the dealerThe hole card flips in 3D and the dealer draws to 16 and stands on every 17. A blackjack pays 3 to 2, a push returns your bet, and the float text shows what you won or lost.
- 6Keep playingDeal again repeats your last bet, and Change bet lets you pick a new one. When the shoe runs low it is reshuffled. Your chips are remembered the next time you visit.
- 7Use the keyboardH, S and D play the hand, D deals when you are betting, and ? shows the hint.
- 8Make it your ownChange MIN_BET, START_CHIPS, DECKS or the CHIP_VALUES list in the JS panel. For hit-on-soft-17, change the dealer loop in dealerTurn() to also draw on a soft 17.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
score() adds every Ace as 11 and counts them, then demotes Aces to 1 one at a time while the total is over 21. It returns the best total that does not bust and a soft flag that says whether an Ace is still counting as 11, and it recalculates from the whole hand every time a card is added.
The dealer draws until the total is 17 or more and stands on every 17, including a soft 17. For the stricter "hit on soft 17" rule, change the loop in dealerTurn() so it also keeps drawing when score(dealer).total is 17 and the hand is soft.
finish() is the only place money moves. A win returns twice the bet, a blackjack returns 2.5 times the bet (3 to 2 profit), a push returns the bet, and a loss returns nothing. Because the bet is taken when you deal or double, those returns keep the bankroll exact. Chips are multiples of 10, so 3 to 2 never creates a fraction.
It is a push and your bet is returned. When the dealer shows an Ace or a ten-value card, the hole card is peeked at straight after the deal, so a dealer blackjack ends the round immediately.
The hole card has a front and a back with backface-visibility hidden, and the card-inner element rotates 180 degrees on the Y axis when the down class is removed. For dealing, addCard() uses FLIP: it measures the existing cards, adds the new one, and animates each card from its old position, and the new card from the shoe, with the Web Animations API.
buildShoe() creates six decks and applies the Fisher-Yates shuffle, swapping each card with a randomly chosen card at or before it. That is the standard way to get an unbiased permutation. Sorting with Math.random() is not uniform.
Yes. The round is split into named functions, so splitting would mean holding an array of hands instead of one, giving each its own bet, and running doHit and dealerTurn once per hand. Insurance is a side bet offered when the dealer shows an Ace, settled in afterDeal().