Vibe Coding JavaScript Games
Build 15 Interactive Games While Mastering Real-World JavaScript, Game Logic, and AI-Assisted
https://github.com/lsvekis/Vibe-Coding-with-JavaScript-Book
https://lsvekis.github.io/Vibe-Coding-with-JavaScript-Book
What if you could learn JavaScript by building real games—and learn how to think like a developer at the same time?
Vibe Coding JavaScript Games is a hands-on, modern guide to learning JavaScript through the creation of 15 fully playable games, each designed to teach core programming concepts, real-world logic, and AI-assisted development techniques.
This isn’t just another “follow-the-steps” coding book.
It introduces a powerful new approach called Vibe Coding—a method that combines:
- structured thinking
- intentional prompting
- iterative refinement
- and real understanding of how code works
Instead of copying code, you’ll learn how to build systems, understand logic deeply, and use AI as a tool—not a shortcut.
🎮 What You’ll Build
Across 15 chapters, you’ll create progressively more advanced games, including:
- 🎰 Slot Machine (state, randomness, game loops)
- 🃏 Blackjack (turn-based logic, arrays, scoring systems)
- 🎡 Roulette (probability, mapping logic to UI)
- ♠️ Poker (hand evaluation, pattern detection)
- 🦸 Hero Dice (combat systems, leveling, strategy)
- 🚢 Trading & Exploration (dynamic economies, risk systems)
- 🧠 And more advanced, original game systems
Each game introduces new concepts while reinforcing what you’ve already learned.
🧠 What You’ll Learn
This book goes beyond syntax. You’ll master:
- how to design and manage game state
- how to build logic-driven systems
- how to connect UI to behavior
- how to create random yet controlled outcomes
- how to structure reusable functions and components
- how to think in terms of systems, not just code
You’ll also learn how to use AI effectively:
- writing better prompts
- breaking problems into smaller parts
- refining outputs instead of accepting them blindly
- building faster while understanding more
⚡ The Vibe Learning System
Every chapter follows a proven learning framework:
- Curiosity – Understand the problem
- Build – Create a working version
- Explain – Break down how it works
- Refine – Improve using AI and logic
- Extend – Add features and deepen understanding
This ensures you don’t just build projects—you retain and apply what you learn.
💡 Who This Book Is For
- Beginners who want to learn JavaScript in a practical, engaging way
- Developers who want to improve their logic and system design skills
- Creators interested in AI-assisted coding workflows
- Anyone who learns best by building real projects
🚀 What Makes This Book Different
Most coding books teach you what to type.
This book teaches you:
- why it works
- how to think
- and how to build anything next
By the end of this book, you won’t just know JavaScript—you’ll know how to use it to create systems, games, and ideas from scratch.
Chapter 1 — Build Your First Game: Slot Machine Systems
Learning to Think Like a Game Developer with Vibe Coding
🔹 The Real Goal of This Chapter
You are not here to build a slot machine.
You are here to learn:
How to turn logic into an interactive system using JavaScript and AI-assisted thinking.
This chapter introduces the core architecture that every game in this book will use:
- State → Logic → UI → Persistence → Loop
And you’ll learn how to use Vibe Coding to guide that process.
🔹 What Is Vibe Coding (In Practice)
Vibe Coding is not “just prompting AI.”
It’s a loop:
1. Intent
What are you trying to build?
2. Structure
What are the core parts?
3. Prompt
Ask AI for help with specific context
4. Evaluate
Does it behave correctly?
5. Refine
Improve logic, clarity, or UX
⚡ Rule
You are not generating code. You are shaping systems.
🔹 The System We’re Building
Your slot machine consists of:
| Component | Responsibility |
|---|---|
| State | Stores all game data |
| Random Engine | Generates symbols |
| Game Logic | Determines wins/losses |
| UI Renderer | Updates screen |
| Event System | Handles clicks |
| Persistence | Saves progress |
🔹 Step 1 — State (Single Source of Truth)
From your repo (script.js):
let state = {
bankroll: 500,
bet: 20,
reels: ["🍒", "🍋", "⭐"],
symbols: ["🍒", "🍋", "⭐", "💎", "7️⃣"],
history: []
};
🔍 Line-by-Line Breakdown
bankroll: 500
- Player starts with 500 credits
- This value changes every spin
bet: 20
- Default bet amount
- Used in payout calculations
reels: ["🍒", "🍋", "⭐"]
- Represents current visible symbols
- This is what the UI displays
symbols: [...]
- All possible outcomes
- The random system pulls from here
history: []
- Stores previous spins
- Enables features like:
- recent results
- analytics
- UI history panels
🧠 Why This Structure Matters
Everything flows through this object.
If something breaks:
👉 You debug the state.
⚡ Vibe Insight
“If your state is clean, your game will be stable.”
🔹 Step 2 — Random Symbol Generator
function getRandomSymbol() {
const index = Math.floor(Math.random() * state.symbols.length);
return state.symbols[index];
}
🔍 Line-by-Line
Math.random()
- Generates number between 0 and 1
* state.symbols.length
- Scales it to array size
Math.floor(...)
- Converts to integer index
🧠 What This Actually Means
You are mapping:
continuous randomness → discrete outcomes
🎯 Important Concept
This is where:
- fairness
- difficulty
- probability
…are controlled.
⚡ Vibe Insight
“Randomness is a design decision — not an accident.”
🔹 Step 3 — Spin Logic (State Mutation)
function spinReels() {
state.reels = [
getRandomSymbol(),
getRandomSymbol(),
getRandomSymbol()
];
}
🔍 What Happens Here
- Old reel values are replaced
- New ones are generated instantly
🧠 Key Concept: Mutation
You are changing the game world.
That means:
- UI must update
- logic must re-run
⚡ Vibe Insight
“Every interaction = state change.”
🔹 Step 4 — Win Logic (Game Rules)
function checkWin() {
const [a, b, c] = state.reels; if (a === b && b === c) {
return state.bet * 5;
} return 0;
}
🔍 Line-by-Line
[a, b, c] = state.reels
- destructures array for readability
a === b && b === c
- checks for full match
return state.bet * 5
- payout multiplier
🧠 Game Design Insight
This is your reward system.
Changing this changes:
- difficulty
- excitement
- retention
⚡ Vibe Insight
“Rules define experience.”
🔹 Step 5 — Game Loop (Core Engine)
function playGame() {
spinReels(); const winnings = checkWin(); state.bankroll -= state.bet;
state.bankroll += winnings; state.history.unshift({
reels: [...state.reels],
win: winnings
}); render();
saveGame();
}
🔍 Line-by-Line
spinReels()
→ generate outcome
checkWin()
→ evaluate result
bankroll -= bet
→ cost of playing
bankroll += winnings
→ reward
history.unshift(...)
→ add to front of history
render()
→ update UI
saveGame()
→ persist state
🧠 Core Pattern
This is:
Input → Process → Output → Store
Every game in your repo uses this pattern.
🔹 Step 6 — UI Rendering
function render() {
document.getElementById("reel1").textContent = state.reels[0];
document.getElementById("reel2").textContent = state.reels[1];
document.getElementById("reel3").textContent = state.reels[2]; document.getElementById("bankroll").textContent = state.bankroll;
}
🧠 Important Concept
UI does NOT contain logic.
It only reflects state.
⚡ Vibe Insight
“If your UI breaks, your state is wrong — not your HTML.”
🔹 Step 7 — Persistence (localStorage)
function saveGame() {
localStorage.setItem("slotMachine", JSON.stringify(state));
}function loadGame() {
const saved = localStorage.getItem("slotMachine");
if (saved) {
state = JSON.parse(saved);
}
}
🔍 What This Does
- converts state → string
- stores it in browser
- reloads on refresh
🧠 Why This Matters
Without this:
- game resets every reload
- no continuity
With this:
- game feels real
⚡ Vibe Insight
“Memory creates meaning.”
🔹 Event Wiring (User Interaction)
document.getElementById("spinBtn")
.addEventListener("click", playGame);
🧠 What This Means
You are connecting:
- UI → Logic
🔹 Vibe Coding — How You Actually Build This
Here’s how you should have built this with AI:
Prompt 1 — Structure First
“Create a JavaScript object to store slot machine state including bankroll, reels, symbols, and history.”
Prompt 2 — Random System
“Create a function that randomly selects items from an array and returns one value.”
Prompt 3 — Game Logic
“Write a function that checks if all elements in an array match and returns a payout.”
Prompt 4 — Game Loop
“Create a function that runs a full game cycle: spin reels, check win, update bankroll, and store results.”
Prompt 5 — UI Binding
“Update DOM elements based on a state object for a slot machine game.”
🔥 Why This Works
You are not asking AI to:
“build a game”
You are asking it to:
- solve small systems
- build pieces
- connect logic
⚡ Vibe Insight
“Great developers don’t ask for solutions — they ask for components.”
🔹 Exercises (Advanced Learning)
🧪 Exercise 1 — Two Match Logic
Add:
if (a === b || b === c || a === c)
→ small win
🧪 Exercise 2 — Weighted Symbols
Make 💎 rare:
- duplicate common symbols
- reduce rare ones
🧪 Exercise 3 — Jackpot System
- add
state.jackpot - increase every spin
- win on 777
🧪 Exercise 4 — Animation Layer
- delay symbol reveal
- simulate spinning
🧪 Exercise 5 — Loss Protection
- track losing streak
- increase payout chance
🔹 Reflection (Vibe Learning)
Answer:
- Where does randomness enter the system?
- What controls player reward?
- What would break if state was removed?
- How would you expand this to 5 reels?
🔹 What You Actually Learned
You now understand:
- state-driven systems
- randomness control
- game loops
- UI synchronization
- persistence
- AI-assisted development workflows
⚡ Final Insight
“You didn’t build a slot machine.
You built your first game engine.”
🔜 Next Chapter
Blackjack Command
You’ll learn:
- multi-step decisions
- player vs dealer logic
- state branching
Chapter 2 — Blackjack Command
Build a Turn-Based Card Game with State, Rules, and Decision Logic
In Chapter 1, you built a slot machine and learned one of the most important lessons in game development:
a game is state + rules + input + output
That idea continues here, but Blackjack introduces something new:
decision flow
A slot machine is mostly event-driven randomness.
Blackjack is different. The player makes choices. The game responds. The dealer follows rules. The game must evaluate outcomes at multiple stages.
That makes Blackjack one of the best early JavaScript games you can build, because it teaches you how to manage:
- arrays of objects
- multi-step game state
- rule-based turns
- score calculation
- user actions that change future outcomes
- branching logic
This is where you stop thinking only in terms of “features” and start thinking in terms of systems that evolve over time.
The Vibe Learning Focus for This Chapter
This chapter uses the Vibe Learning format:
Curiosity
How do real card games keep track of cards, scores, turns, and outcomes?
Build
Create a working Blackjack game with dealing, hit, stand, and dealer logic.
Explain
Break the code into systems so you understand what each part does and why it exists.
Refine
Use Vibe Coding prompts to improve logic, readability, feedback, and gameplay.
Extend
Add features that move the project from a simple demo toward a fuller game.
The Goal of This Chapter
You are not just building Blackjack.
You are learning how to build a turn-based rules engine.
That means learning how to model:
- a deck
- a hand
- a score
- a round
- player decisions
- dealer behavior
- game outcomes
If you understand this chapter deeply, you will be much more prepared for later games like:
- Poker Showdown
- Hero Dice: Villain Crisis
- any RPG combat system
- any tactical turn-based game
What We Are Building
In the repo version of Blackjack Command, the player can:
- place a bet
- deal a new hand
- hit to take another card
- stand to end their turn
- compare against the dealer
- win, lose, or push
- track chip balance over time
This game teaches real JavaScript architecture because it must coordinate:
- card data
- deck data
- player hand
- dealer hand
- score logic
- UI state
- round state
System Overview
Before we look at code, let’s break the game into the main systems.
| System | Responsibility |
|---|---|
| Deck System | Creates and shuffles cards |
| Hand System | Stores player and dealer cards |
| Value System | Calculates Blackjack scores |
| Turn System | Controls whether the player or dealer acts |
| Outcome System | Determines win, loss, bust, or push |
| Bankroll System | Tracks chips and bets |
| Render System | Updates the UI |
That’s the Vibe Coding mindset:
Do not think “page.”
Think “systems.”
Step 1 — Model the State
A Blackjack game needs more structure than a slot machine.
A typical setup in your project looks like this:
let deck = [];
let player = [];
let dealer = [];
let inRound = false;let state = {
chips: 300,
bet: 25
};
What Each Part Does
deck = []
This stores the remaining cards that have not been drawn yet.
player = []
This stores the player’s current hand.
dealer = []
This stores the dealer’s current hand.
inRound = false
This controls whether a hand is active.
It prevents things like:
- dealing twice during a round
- changing the bet mid-hand
- hitting after the round is over
state = { chips, bet }
This stores persistent player economy data:
- how many chips are available
- what the current wager is
Why This Matters
In Chapter 1, the state was mostly one object.
Here, it is split between:
- persistent round-independent state like chips and bet
- temporary round state like deck, player hand, dealer hand
That is a more advanced design pattern.
Developer Insight
Not all game state belongs in one giant object.
Sometimes it is cleaner to separate:
- long-term state
- short-term round state
- UI state
That makes debugging easier.
Vibe Coding Prompt: Structure the State
Use a prompt like this early:
Create the JavaScript state structure for a blackjack game. Include deck, player hand, dealer hand, a flag for whether a round is active, and persistent chip and bet values.
Why this prompt works:
- it asks for structure, not full code
- it helps AI focus on architecture
- it gives you a clean starting point
Step 2 — Create the Deck
Blackjack needs a full card deck, so we need a function to generate one.
A typical version looks like this:
function makeDeck() {
const suits = ["♠", "♥", "♦", "♣"];
const values = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]; deck = []; for (const suit of suits) {
for (const value of values) {
deck.push({ value, suit });
}
} for (let i = deck.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[deck[i], deck[j]] = [deck[j], deck[i]];
}
}
Code Breakdown
const suits = [...]
Defines the four suits in a standard deck.
const values = [...]
Defines all card ranks.
deck = []
Resets the deck before rebuilding it.
Nested loops
This creates every possible suit-value combination:
- A♠
- 2♠
- 3♠
- …
- K♣
That gives 52 total cards.
Shuffle loop
This is the Fisher-Yates shuffle pattern.
for (let i = deck.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[deck[i], deck[j]] = [deck[j], deck[i]];
}
This randomly swaps cards so the deck is shuffled fairly.
Why This Matters
You are now working with arrays of objects, which is a major step up.
Each card is not just text. It is a data object:
{ value: "K", suit: "♣" }
This makes the code much easier to reason about later.
Developer Insight
A game becomes easier to build when your data matches the real thing.
A card is not “just a string.”
It has parts.
That is one of the most important habits in programming:
model the world clearly.
Vibe Coding Prompt: Generate and Shuffle a Deck
Write a JavaScript function that creates a standard 52-card deck as an array of objects with value and suit properties, then shuffles it using Fisher-Yates.
Why this prompt is good:
- it names the data format
- it names the shuffle algorithm
- it reduces vague output
Step 3 — Draw Cards into Hands
Once the deck exists, we need to deal cards.
A compact version looks like this:
function drawCard(hand) {
if (!deck.length) {
makeDeck();
} hand.push(deck.pop());
}
What This Does
if (!deck.length)
If the deck is empty, rebuild and reshuffle it.
hand.push(deck.pop())
Take the top card from the deck and add it to the chosen hand.
Because the function receives a hand as a parameter, it can be reused for:
- player
- dealer
Why This Matters
This is your first strong example of a reusable action function.
Instead of writing:
drawPlayerCard()drawDealerCard()
You create one function that works for both.
That is cleaner and more scalable.
Vibe Coding Insight
A good prompt often asks AI for a generic helper function, not a one-off solution.
Better:
Create a reusable function that removes one card from the deck and adds it to a specified hand array.
Worse:
Make the player get a card.
The better prompt produces more reusable code.
Step 4 — Calculate Hand Value
Blackjack scoring is where the game becomes interesting, because not all cards are equal.
A typical scoring function looks like this:
function handValue(hand) {
let total = 0;
let aces = 0; for (const card of hand) {
if (card.value === "A") {
aces++;
total += 11;
} else if (["K", "Q", "J"].includes(card.value)) {
total += 10;
} else {
total += Number(card.value);
}
} while (total > 21 && aces > 0) {
total -= 10;
aces--;
} return total;
}
Line-by-Line Logic
let total = 0
The running score of the hand.
let aces = 0
Tracks how many aces are currently being counted as 11.
Loop through cards
Each card adds value to the hand.
Ace
if (card.value === "A") {
aces++;
total += 11;
}
Start by treating Ace as 11.
Face cards
else if (["K", "Q", "J"].includes(card.value)) {
total += 10;
}
All face cards count as 10.
Number cards
else {
total += Number(card.value);
}
Convert the string like "7" into the number 7.
The Most Important Part: Flexible Ace Logic
while (total > 21 && aces > 0) {
total -= 10;
aces--;
}
If the total is too high and we still have aces counted as 11, we reduce one ace from 11 to 1 by subtracting 10.
Example:
- Ace + 9 + 8 = 28
- too high
- convert Ace from 11 to 1
- new total = 18
Why This Matters
This function is the heart of Blackjack.
It teaches:
- iteration
- conditional logic
- special-case rules
- score correction after evaluation
This is a strong example of game rule encoding.
Developer Insight
A lot of game logic works like this:
- calculate the optimistic version first
- correct it if rules require adjustment
That pattern appears again in:
- combat modifiers
- inventory limits
- movement constraints
- AI threat evaluation
Vibe Coding Prompt: Ace Logic
Write a JavaScript function to calculate blackjack hand values, including special handling so aces count as 11 unless that would cause the hand to bust, in which case they count as 1.
Why this prompt works:
- it explains the rule directly
- it forces AI to solve the real problem, not a simplified one
Step 5 — Start a New Round
Now we combine earlier systems into a round setup.
A typical deal action looks like this:
function dealRound() {
if (state.chips < state.bet) return; makeDeck();
player = [];
dealer = [];
inRound = true; state.chips -= state.bet; drawCard(player);
drawCard(dealer);
drawCard(player);
drawCard(dealer); render(); if (handValue(player) === 21) {
const dealerValue = handValue(dealer); if (dealerValue === 21) {
settleRound("Push on blackjack.", state.bet);
} else {
settleRound("Blackjack! Paid 3:2.", Math.floor(state.bet * 2.5));
}
}
}
What This Does
Check affordability
The player cannot deal a hand if they do not have enough chips.
Reset round state
- new deck
- empty hands
- round becomes active
Deduct the bet
This simulates putting chips on the table.
Deal two cards each
Standard Blackjack opening hand.
Render UI
Update what the player sees.
Check immediate blackjack
If the player hits 21 right away, resolve instantly.
Why This Matters
This function is the first strong example of multi-stage orchestration.
It doesn’t just do one thing. It coordinates:
- validation
- setup
- economy
- dealing
- rendering
- special outcome checking
This is exactly how many real game systems work.
Step 6 — Hit Logic
When the player asks for another card, the game must:
- draw a card
- update the UI
- test for bust
A typical version:
function hitPlayer() {
drawCard(player);
render(); const total = handValue(player); if (total > 21) {
settleRound(`Bust at ${total}.`, 0);
}
}
Logic Breakdown
drawCard(player)
Adds one card to the player’s hand.
render()
Shows the new hand.
handValue(player)
Recalculates the new total.
if (total > 21)
If the player exceeds 21, the round ends.
Why This Matters
This is one of the cleanest examples of action → consequence.
Player action:
- hit
System consequence:
- new card
- new total
- possible loss
This is the core of interactive game design.
Step 7 — Dealer Logic
The dealer behaves differently from the player.
The player chooses.
The dealer follows fixed rules.
A common implementation:
function standPlayer() {
while (handValue(dealer) < 17) {
drawCard(dealer);
} const playerTotal = handValue(player);
const dealerTotal = handValue(dealer); render(); if (dealerTotal > 21 || playerTotal > dealerTotal) {
settleRound(`Player wins ${playerTotal} vs ${dealerTotal}.`, state.bet * 2);
} else if (playerTotal === dealerTotal) {
settleRound(`Push ${playerTotal}-${dealerTotal}.`, state.bet);
} else {
settleRound(`Dealer wins ${dealerTotal} vs ${playerTotal}.`, 0);
}
}
What This Teaches
Dealer AI does not need to be smart
It only needs to follow clear rules.
while (handValue(dealer) < 17)
Dealer keeps drawing until reaching 17 or more.
Outcome comparison
After both totals are known:
- bust = lose
- higher total wins
- tie = push
Developer Insight
Many games do not need complex AI.
They need:
- predictable behavior
- rule consistency
- understandable outcomes
That is often more important than complexity.
Step 8 — Settling the Round
Now we need a reusable round-ending function.
A typical version:
function settleRound(message, payout) {
inRound = false;
state.chips += payout;
render();
log(message);
}
What This Does
inRound = false
Disables hit and stand actions.
state.chips += payout
Applies:
- win
- push
- blackjack bonus
- loss
render()
Refreshes the display.
log(message)
Tells the player what happened.
Why This Matters
This is a centralized resolution function.
Instead of scattering “round over” logic everywhere, you funnel all outcomes into one place.
That is cleaner and easier to maintain.
Step 9 — Render the Hands and Scores
Blackjack relies heavily on visual clarity. The player must see:
- their hand
- dealer hand
- score
- chip count
- button availability
A rendering pattern often looks like this:
function render() {
chipsEl.textContent = state.chips;
betEl.textContent = state.bet;
playerScoreEl.textContent = handValue(player); if (inRound) {
dealerScoreEl.textContent = handValue(dealer.slice(1));
} else {
dealerScoreEl.textContent = handValue(dealer);
} renderCards(playerCardsEl, player);
renderCards(dealerCardsEl, dealer, inRound); hitBtn.disabled = !inRound;
standBtn.disabled = !inRound;
dealBtn.disabled = inRound;
}
Important UI Ideas Here
Hide one dealer card during the round
That creates suspense and follows Blackjack convention.
Disable buttons based on state
This prevents invalid actions.
Examples:
- cannot hit before dealing
- cannot deal again mid-round
- cannot stand after round ends
Why This Matters
This is your first strong lesson in UI as rule enforcement.
The UI is not just presentation.
It is also part of the system that guides valid play.
Vibe Coding Prompt: Render and Disable Buttons
Update the Blackjack UI from state, including chips, bet, player score, dealer score, rendered cards, and button disabling rules based on whether a round is active.
This works because it asks for:
- outputs
- dependencies
- restrictions
That leads to better structured code.
Step 10 — Event Wiring
Finally, the player’s buttons must connect to the rule system.
A typical event layer:
dealBtn.addEventListener("click", dealRound);
hitBtn.addEventListener("click", hitPlayer);
standBtn.addEventListener("click", standPlayer);
betPlusBtn.addEventListener("click", increaseBet);
betMinusBtn.addEventListener("click", decreaseBet);
This is where UI meets logic.
Each button:
- triggers one specific action
- that action changes state
- the game rerenders
The Full Gameplay Loop
Blackjack is a more advanced loop than the slot machine.
Start
- player places bet
- clicks Deal
During round
- player chooses Hit or Stand
Dealer phase
- dealer draws until 17+
Resolution
- compare totals
- update chips
- show result
- unlock next round
That is a real turn-based game loop.
Vibe Coding: How to Build This Well
This is where your book can really stand out.
Do not ask AI:
Build a blackjack game.
That is too broad.
Instead, build in layers.
Recommended Vibe Coding Build Sequence
Prompt 1 — Model the Data
Create the core JavaScript variables and state structure for a blackjack game, including deck, player hand, dealer hand, bet amount, chips, and round status.
Prompt 2 — Build the Deck System
Write a function that creates and shuffles a 52-card deck as objects with value and suit properties.
Prompt 3 — Calculate Scores
Write a blackjack hand scoring function that correctly handles face cards and flexible ace values.
Prompt 4 — Build Round Actions
Create separate JavaScript functions for deal, hit, and stand in a blackjack game, using the deck and hand scoring functions.
Prompt 5 — Render the UI
Update DOM elements for the blackjack table, showing cards, scores, chips, and enabling or disabling buttons depending on round state.
Prompt 6 — Improve the UX
Improve the blackjack game UX by adding clearer messages, better button states, and more visual separation between player and dealer areas.
Why This Prompt Strategy Works
Because each prompt asks AI to solve one layer of the system.
That means:
- better code quality
- easier debugging
- clearer learning
- less messy outputs
Vibe Coding Rule for This Chapter
Ask AI for systems, not miracles.
That is what makes Vibe Coding useful instead of chaotic.
Common Mistakes in Blackjack Projects
This section is important because best-selling books help readers avoid failure, not just build success.
Mistake 1 — Treating cards like plain strings
That makes score logic harder later.
Mistake 2 — Not separating round state from persistent state
Then the code becomes hard to control.
Mistake 3 — Writing dealer logic inside UI events
That mixes systems together.
Mistake 4 — Forgetting ace correction
This breaks core game rules.
Mistake 5 — Letting invalid actions happen
A player should not be able to hit when no round is active.
Exercises
These exercises are designed to deepen real understanding, not just add busywork.
Exercise 1 — Add Soft/Hard Hand Labels
Show whether the player hand is:
- soft 17
- hard 18
- etc.
This teaches:
- score interpretation
- richer UI output
Exercise 2 — Add a Double Down Feature
Rules:
- only on first decision
- double the bet
- draw one card
- automatically stand
This teaches:
- action restrictions
- temporary state rules
- more complex branching
Exercise 3 — Add Win Statistics
Track:
- total wins
- total losses
- pushes
- blackjacks
This teaches:
- persistent stats
- extra render layers
Exercise 4 — Improve Dealer Reveal
Hide the first dealer card until the round ends.
Then explain in your own words:
- why suspense matters
- how hidden information changes player experience
Exercise 5 — Add localStorage
Save:
- chips
- bet
- stats
This teaches:
- continuity
- product thinking
- state persistence
Challenge Mode
Use Vibe Coding to extend the game with one of these prompts:
Prompt: Add Split Hands
Add split hand logic to the blackjack game so the player can split matching opening cards into two hands and play them one at a time.
Prompt: Add Insurance
Add optional insurance to the blackjack game when the dealer shows an ace, and resolve the side bet correctly.
Prompt: Add Table Themes
Add a table theme selector that changes the visual styling of the blackjack table without changing the game logic.
Reflection Questions
Write your answers before moving to Chapter 3.
- Why is Blackjack more complex than the slot machine?
- What is the most important function in this game, and why?
- Why is ace logic a good example of rule encoding?
- How does the dealer system differ from player input?
- Which parts of this chapter could be reused in Poker Showdown?
Vibe Learning Summary
By the end of this chapter, you should understand that Blackjack is not just a card game.
It is a model of turn-based programming.
You learned how to build and reason about:
- arrays of objects
- reusable helper functions
- rule-based score calculation
- branching game states
- turn resolution
- outcome handling
- UI-state synchronization
That is a major step forward.
What You Learned That Transfers Forward
This chapter gives you reusable patterns for later projects:
Deck and hand logic
Useful for:
- Poker
- RPG loot systems
- inventory generators
Turn sequence logic
Useful for:
- Hero Dice
- tactical combat
- enemy AI rounds
Outcome comparison
Useful for:
- battle resolution
- score contests
- ranking systems
State-driven button control
Useful for:
- any UI-heavy game
- management sims
- puzzle games
Final Insight
In Chapter 1, you learned that a game is state + rules + input + output.
In Chapter 2, you learn something deeper:
A turn-based game is a conversation between systems.
The player acts.
The rules respond.
The game state changes.
The UI tells the story of that change.
That is real game development.
Next Chapter
In Chapter 3 — Roulette Frontier, you’ll move into:
- probability-driven outcomes
- mapping logic to visuals
- payout structures
- syncing game results with interface feedback
That chapter will help you think more deeply about randomness, fairness, and presentation.
Chapter 3 — Roulette Frontier
Build a Probability Game with Mapped Outcomes, Betting Logic, and Visual Feedback
In Chapter 1, you built a slot machine and learned the basics of state, randomness, and UI updates.
In Chapter 2, Blackjack Command introduced turn-based logic, branching decisions, and rule-driven scoring.
Now we move into a different kind of game:
a game where the core tension comes from probability, betting, and presentation
Roulette is powerful for learning JavaScript because it teaches you how to connect:
- a visible interface
- a hidden result
- a mapped set of outcomes
- payout rules
- player bets
- persistent bankroll state
It also introduces a deeper lesson:
not all randomness feels trustworthy unless the presentation matches the logic
That is exactly why Roulette Frontier matters in this book.
It is not just about spinning a wheel. It is about making the logic and the visible result feel aligned.
The Vibe Learning Focus for This Chapter
This chapter follows the same Vibe Learning structure:
Curiosity
How do roulette games map numbers, colors, and bets into a system that feels fair and understandable?
Build
Create a roulette game with:
- bankroll
- bet controls
- a visible wheel
- result history
- payout logic
- saved progress
Explain
Break down the code into:
- probability model
- wheel order
- rotation logic
- payout system
- UI feedback
Refine
Use Vibe Coding prompts to improve:
- clarity
- alignment
- visual trust
- reliability
Extend
Add deeper betting options and better UX.
The Real Goal of This Chapter
You are not just building roulette.
You are learning how to build a mapped outcome system.
That means:
- every number belongs somewhere
- every number has a color
- every bet must connect to clear rules
- the visible UI must match the hidden logic
- the player must trust the result
This is one of the most important ideas in game development:
A game can be logically correct and still feel wrong if the presentation does not support the logic.
What We Are Building
In Roulette Frontier, the player can:
- place a bet amount
- choose a bet type:
- red
- black
- even
- odd
- single number
- spin the wheel
- see the result
- receive a payout if the bet wins
- track recent spin history
- keep bankroll progress with
localStorage
This chapter teaches strong JavaScript skills because it combines:
- number mapping
- visual state
- persistent state
- conditional payouts
- interface feedback
System Overview
Before code, let’s break Roulette Frontier into systems.
| System | Responsibility |
|---|---|
| Bankroll System | Tracks player money |
| Bet System | Stores bet amount and bet type |
| Wheel Order System | Defines number placement |
| Color Mapping System | Determines red, black, green |
| Spin System | Generates a winning number |
| Payout System | Decides win/loss and returns money |
| UI System | Updates wheel, labels, history, and messages |
| Persistence System | Saves bankroll and last results |
Again, this is Vibe Coding thinking:
build the game as connected systems, not as one giant blob of code
Step 1 — Persistent State
A roulette game needs to remember more than just one result. It needs to store player progress and UI-related state.
A typical structure from this project looks like this:
const state = loadState();
let selectedType = state.selectedType || "red";
let spinning = false;
let wheelRotation = Number.isFinite(state.wheelRotation) ? state.wheelRotation : 0;
And the stored object looks like this:
{
bankroll: 500,
betAmount: 20,
numberPick: 17,
selectedType: "red",
wheelRotation: 0,
lastResult: null,
lastNet: 0,
history: []
}
What Each Part Means
bankroll
How much money the player currently has.
betAmount
The amount risked on the next spin.
numberPick
Used only when the player selects a single number bet.
selectedType
Stores which bet type is active.
wheelRotation
Stores the current rotation angle so the wheel can continue from its last state.
lastResult
Stores the most recent winning number and its color.
lastNet
Stores the profit or loss from the last spin.
history
Stores recent past spins for the result board.
Why This Matters
This is more advanced than Chapter 1 because the state now includes both:
- gameplay information
- interface memory
That makes the experience feel more polished.
Developer Insight
A lot of modern interfaces store not only game state, but also presentation continuity.
Examples:
- wheel rotation
- selected tab
- last filter
- saved theme
- expanded menu state
That is a product-level design mindset.
Vibe Coding Prompt: State Design
Create the saved state structure for a JavaScript roulette game. Include bankroll, bet amount, selected bet type, picked number, wheel rotation, last result, last profit or loss, and recent spin history.
Why this is a strong prompt:
- it asks for a system blueprint
- it gives AI clear constraints
- it avoids vague all-in-one code dumps
Step 2 — The Wheel Order
Roulette is not random numbers in a random list. The wheel has a specific order.
In the project, that order is stored like this:
const WHEEL_ORDER = [
0, 32, 15, 19, 4, 21, 2, 25, 17, 34, 6, 27, 13, 36, 11, 30, 8, 23,
10, 5, 24, 16, 33, 1, 20, 14, 31, 9, 22, 18, 29, 7, 28, 12, 35, 3, 26
];
Why This Array Matters
This array is the wheel map.
It answers:
- what numbers exist
- how many there are
- where they appear around the circle
Without this array, you do not have a roulette wheel.
You only have a random number generator.
That distinction matters.
Developer Insight
A lot of games depend on a “map array” like this:
- board games
- tile maps
- level layouts
- attack order
- enemy waves
- wheel positions
If you understand this array, you understand the geometry of the system.
Step 3 — Color Mapping
Roulette also needs to know whether a number is red, black, or green.
A typical implementation looks like this:
const RED_NUMBERS = new Set([
1, 3, 5, 7, 9, 12, 14, 16, 18,
19, 21, 23, 25, 27, 30, 32, 34, 36
]);function colorFor(number) {
if (number === 0) return "green";
return RED_NUMBERS.has(number) ? "red" : "black";
}
Code Breakdown
new Set([...])
A Set is used because it makes membership checks clean and fast.
if (number === 0)
Roulette zero is green.
RED_NUMBERS.has(number)
If the number is in the red set, return red. Otherwise it must be black.
Why This Matters
This is a strong example of data-driven logic.
Instead of writing:
if (number === 1 || number === 3 || number === 5 ...)
you build a data structure once and reuse it cleanly.
That is better code and better design.
Vibe Coding Prompt: Color Logic
Create a JavaScript function for roulette that returns green for 0, red for official red numbers, and black for all other numbers.
Why it works:
- clearly defines the edge case
- specifies the expected outputs
- turns vague behavior into testable logic
Step 4 — Building the Wheel UI
In the simplified premium version, the wheel is built with HTML/CSS slots instead of a more fragile canvas labeling system.
A typical function looks like this:
function buildWheel(highlightNumber = null) {
wheelEl.innerHTML = ""; WHEEL_ORDER.forEach((num, index) => {
const slot = document.createElement("div");
slot.className = `slot ${colorFor(num)}${num === highlightNumber ? " active" : ""}`;
slot.style.transform = `rotate(${index * SEGMENT_DEG}deg)`; const label = document.createElement("div");
label.className = "slot-label";
label.textContent = num; slot.appendChild(label);
wheelEl.appendChild(slot);
}); wheelEl.style.transform = `rotate(${wheelRotation}deg)`;
}
Line-by-Line Logic
wheelEl.innerHTML = ""
Clear any old slots before rebuilding.
WHEEL_ORDER.forEach(...)
Create one visible slot per number.
slot.className = ...
Apply:
- red / black / green styles
- active highlight if this was the winning number
slot.style.transform = rotate(...)
Place each slot around the wheel using its index.
label.textContent = num
Show the number visibly.
wheelEl.style.transform = rotate(...)
Rotate the entire wheel to its current angle.
Why This Matters
This is a strong lesson in presentation driven by data.
You are not hardcoding each slot manually.
You are generating the interface from a system map.
That means:
- fewer mistakes
- easier updates
- better alignment with logic
Developer Insight
When UI can be generated from data, it becomes more reliable.
That is especially useful in:
- game boards
- enemy panels
- card hands
- upgrade trees
- result histories
Step 5 — Bet Type and Payout Display
The player needs immediate feedback about what kind of bet is selected and what the expected payout is.
A rendering helper might look like this:
function payoutText(type) {
return type === "number" ? "36x total return" : "2x total return";
}
And the UI update function:
function renderButtons() {
document.querySelectorAll(".bet-btn").forEach(btn => {
btn.classList.toggle("selected", btn.dataset.type === selectedType);
}); selectedBetEl.textContent =
selectedType === "number"
? `Single Number (${Number(numberPickEl.value) || 0})`
: selectedType.charAt(0).toUpperCase() + selectedType.slice(1); selectedPayoutEl.textContent = payoutText(selectedType);
}
What This Does
Highlight selected bet type
The player sees what bet is currently active.
Update label text
The UI reflects either:
- Red
- Black
- Even
- Odd
- Single Number (17)
Show payout rule
The player understands the risk/reward before spinning.
Why This Matters
A good game interface reduces uncertainty about rules.
This is especially important in betting systems because confusion feels unfair.
Step 6 — Spinning the Wheel
Now we reach the visible motion system.
First, the game needs to know where a number appears on the wheel:
function indexOfNumber(number) {
return WHEEL_ORDER.indexOf(number);
}
Then it needs to calculate how far to rotate:
function computeFinalRotation(number) {
const idx = indexOfNumber(number);
const targetMod = (360 - (idx * SEGMENT_DEG)) % 360;
const currentMod = ((wheelRotation % 360) + 360) % 360;
const delta = (targetMod - currentMod + 360) % 360;
return wheelRotation + 1440 + delta;
}
Why This Looks More Complex
Because the game must solve two problems:
1. Put the correct number under the fixed pointer
That is what targetMod is for.
2. Spin naturally from the current position
That is why the code uses:
- current rotation
- modular math
- several extra turns (
1440, or four full spins)
Line-by-Line Explanation
const idx = indexOfNumber(number)
Find where the number sits in the wheel order.
const targetMod = ...
Figure out the angle required to place that slot under the pointer.
const currentMod = ...
Normalize the current wheel angle into a 0–359 range.
const delta = ...
Compute how much more rotation is needed to land correctly.
return wheelRotation + 1440 + delta
Add four full turns plus the exact needed adjustment.
This gives a result that:
- spins dramatically
- still lands on the correct number
Developer Insight
This is a classic example of animation math that serves logic.
The motion is not random.
It is a calculated performance layer on top of a fixed outcome.
That is how many games work.
Examples:
- battle animations
- loot chest reveals
- wheel spins
- slot reels
- card flips
The outcome is known first.
The animation is designed to deliver it convincingly.
Vibe Coding Prompt: Rotation Math
Given a roulette wheel order array and a fixed pointer at the top, calculate the final rotation angle needed so a chosen number lands under the pointer after several full spins.
This is a great prompt because it asks AI for a math-specific helper, not an entire game.
Step 7 — Resolving the Spin
Once the result number is known, the game needs to determine whether the bet wins.
A core resolution function looks like this:
function resolveSpin(number, type, bet, numberPick) {
const color = colorFor(number);
let payout = 0; if (type === "red" && color === "red") payout = bet * 2;
if (type === "black" && color === "black") payout = bet * 2;
if (type === "even" && number !== 0 && number % 2 === 0) payout = bet * 2;
if (type === "odd" && number % 2 === 1) payout = bet * 2;
if (type === "number" && number === numberPick) payout = bet * 36; state.bankroll -= bet;
state.bankroll += payout; state.lastResult = { number, color };
state.lastNet = payout - bet;
state.history.unshift({ number, color, type, bet, numberPick, net: state.lastNet });
state.history = state.history.slice(0, 20);
}
Code Breakdown
const color = colorFor(number)
Find the result color.
let payout = 0
Assume loss unless proven otherwise.
Conditional payouts
Each bet type checks its own win rule.
Red / Black
Straight color match.
Even
Must be even and not zero.
Odd
Must be odd.
Single Number
Must match exactly.
Important Detail: Net Result
state.lastNet = payout - bet;
This makes result messaging clearer.
Examples:
- bet 20, payout 40 → net +20
- bet 20, payout 0 → net -20
This is better than only showing payout because it tells the player the actual outcome.
Why This Matters
This function is the outcome engine.
It:
- applies rules
- changes bankroll
- records result state
- updates history data
That is the heart of the roulette game.
Step 8 — The Spin Action
Now we connect:
- validation
- animation
- outcome resolution
A typical spin function:
function spin() {
const bet = Number(betAmountEl.value);
const numberPick = Number(numberPickEl.value); if (!bet || bet < 10) {
resultBannerEl.className = "result-banner lose";
resultBannerEl.textContent = "Enter a valid bet of at least 10.";
return;
} if (bet > state.bankroll) {
resultBannerEl.className = "result-banner lose";
resultBannerEl.textContent = "You do not have enough bankroll for that bet.";
return;
} if (selectedType === "number" && (Number.isNaN(numberPick) || numberPick < 0 || numberPick > 36)) {
resultBannerEl.className = "result-banner lose";
resultBannerEl.textContent = "Choose a number from 0 to 36.";
return;
} spinning = true;
renderStats(); const result = Math.floor(Math.random() * 37);
wheelRotation = computeFinalRotation(result);
wheelEl.style.transform = `rotate(${wheelRotation}deg)`; setTimeout(() => {
resolveSpin(result, selectedType, bet, numberPick);
saveState();
spinning = false;
renderStats();
}, 3650);
}
What This Teaches
Validation first
Before spinning:
- bet must be valid
- bankroll must cover it
- single-number picks must be legal
Freeze the UI
spinning = true disables repeated interaction during the animation.
Generate the result
const result = Math.floor(Math.random() * 37);
That gives 0–36.
Animate to that result
The wheel is rotated to land on the chosen number.
Resolve after delay
Only after the animation finishes:
- bankroll updates
- history updates
- labels rerender
Developer Insight
This is a good example of separating event timing from game logic.
The result is chosen immediately.
The player sees it later.
That creates anticipation without changing the rule system.
Step 9 — Result Rendering
After a spin, the player should instantly understand:
- what number hit
- what color it was
- whether they won or lost
- how much they gained or lost
That is what the result banner does:
function renderBanner() {
if (!state.lastResult) {
resultBannerEl.className = "result-banner neutral";
resultBannerEl.textContent = "Place a bet and spin.";
return;
} const r = state.lastResult;
resultBannerEl.className = `result-banner ${state.lastNet >= 0 ? "win" : "lose"}`;
resultBannerEl.textContent =
`Wheel landed on ${r.number} (${r.color.toUpperCase()}) · ${state.lastNet >= 0 ? "Win" : "Loss"} ${Math.abs(state.lastNet)}`;
}
Why This Matters
The player should never need to mentally decode the game state.
A strong interface translates the logic into meaning instantly.
That is a best-seller-level teaching point because many beginner books explain how to code, but not how to communicate outcomes.
Step 10 — History Rendering
The result history helps make the game feel real and trackable.
A typical rendering function:
function renderHistory() {
historyEl.innerHTML = ""; if (!state.history.length) {
historyEl.innerHTML = "<div class='history-item'>No spins yet.</div>";
return;
} state.history.slice(0, 12).forEach(item => {
const row = document.createElement("div");
row.className = "history-item";
row.innerHTML = `
<div>
<div><strong>${item.number}</strong> ${item.color.toUpperCase()}</div>
<div class="muted">Bet: ${item.type}${item.type === "number" ? ` (${item.numberPick})` : ""} · ${item.bet}</div>
</div>
<div class="history-pill ${item.color}">
${item.net >= 0 ? "+" + item.net : item.net}
</div>
`;
historyEl.appendChild(row);
});
}
What This Teaches
- how to render repeated UI from data
- how to format result summaries
- how to build a “story” of previous actions
This pattern shows up everywhere in modern apps:
- notifications
- score history
- transaction lists
- combat logs
- order history
Vibe Coding: How to Build This Chapter Well
This chapter is a perfect example of where Vibe Coding must be intentional.
Do not ask:
Make a roulette game.
That is too vague and often creates mismatched logic and visuals.
Instead, prompt in layers.
Recommended Vibe Coding Sequence
Prompt 1 — Model the Game State
Create the JavaScript state structure for a roulette game, including bankroll, bet amount, selected bet type, picked number, recent history, and last result.
Prompt 2 — Create the Wheel Order
Store the roulette wheel number order in a JavaScript array and write a helper to find the index of a number in that order.
Prompt 3 — Build the Color System
Write a JavaScript helper function that returns red, black, or green for roulette numbers, using the official red number set and green for zero.
Prompt 4 — Build the Payout Logic
Create a function that resolves roulette bets for red, black, odd, even, and single-number bets, then updates bankroll and history.
Prompt 5 — Solve the Rotation Math
Calculate the final CSS rotation needed to spin a roulette wheel several full turns and land a selected number under a fixed pointer.
Prompt 6 — Improve the UI Rendering
Render roulette history, selected bet info, last result, and bankroll from state in a polished JavaScript UI.
Why This Prompt Strategy Works
Because each prompt asks AI to build a specific subsystem.
That produces:
- cleaner code
- easier debugging
- more reusable logic
- better understanding
Vibe Coding Rule for This Chapter
When logic and visuals must match, prompt for them separately, then test them together.
That is the key lesson here.
Common Mistakes in Roulette Projects
This section matters because readers trust books that help them avoid pain.
Mistake 1 — Using random numbers without a real wheel map
Then the interface feels fake.
Mistake 2 — Showing a wheel result that does not match the actual logic
This destroys player trust.
Mistake 3 — Hardcoding messy payout conditions everywhere
Instead, centralize them.
Mistake 4 — Forgetting zero rules
Zero is green and is neither odd nor even.
Mistake 5 — Making the animation more complex than necessary
A simpler wheel that is always accurate is better than a flashy one that feels wrong.
Exercises
These exercises are designed to help the reader think like a builder.
Exercise 1 — Add Low / High Bets
Add:
- 1–18
- 19–36
This teaches:
- range-based conditions
- expanding payout systems
Exercise 2 — Add Dozen Bets
Allow bets on:
- 1–12
- 13–24
- 25–36
This teaches:
- grouped probability rules
- scalable condition handling
Exercise 3 — Add Better Bankroll Messaging
Show messages like:
- “You won 20”
- “You lost 20”
- “Exact number hit!”
This teaches:
- stronger player feedback
- message formatting
Exercise 4 — Add Win / Loss Statistics
Track:
- total spins
- total wins
- total losses
- biggest win
This teaches:
- persistent analytics state
Exercise 5 — Build a Flat Table Layout
Add a visual betting board with clickable number cells.
This teaches:
- UI mapping
- interactive selection
- game-table design
Challenge Mode Prompts
Prompt: Add Dozens and Columns
Expand the roulette game so the player can also bet on dozens and columns, and update the payout logic accordingly.
Prompt: Add a Betting Table UI
Create a clickable roulette betting table layout with number cells, color bets, and even/odd areas that update the current selected bet.
Prompt: Add Spin Sound and Win Feedback
Improve the roulette game by adding optional sound effects, a winning highlight, and stronger result messages after each spin.
Reflection Questions
- Why does roulette depend so heavily on trust between logic and visuals?
- What role does the wheel order array play in the game?
- Why is
0a useful example of a special-case rule? - What is the most important function in this chapter, and why?
- Why is a simpler but accurate wheel better than a visually impressive but unreliable one?
Vibe Learning Summary
By the end of this chapter, you should understand that Roulette Frontier is not just a probability game.
It is a mapping game.
You learned how to connect:
- data structures
- visual layout
- random outcomes
- payout conditions
- stored history
- interface trust
That is a major design lesson.
What Transfers Forward
This chapter prepares you for later games in several ways:
Mapping systems
Useful for:
- puzzle boards
- level layouts
- enemy formations
Payout / reward logic
Useful for:
- trading systems
- combat rewards
- progression systems
Visual alignment with logic
Useful for:
- action games
- tactical games
- management interfaces
History rendering
Useful for:
- logs
- stats
- battle histories
- transaction records
Final Insight
In Chapter 1, you learned that games are systems.
In Chapter 2, you learned that turn-based systems are structured conversations.
In Chapter 3, you learn something even more subtle:
Players trust what they can see only when it matches the logic underneath.
That is not just a roulette lesson.
That is a game design lesson.
Next Chapter
In Chapter 4 — Poker Showdown, you’ll step into:
- hand ranking
- pattern detection
- sorted arrays
- multi-stage game flow
That chapter will push you further into algorithmic thinking and reusable scoring systems.
