Liveweave: Generative AI Web Editor & HTML/CSS/JS Playground
Initializing
Liveweave
Web
expand_more
home
Home
Palette
Color Explorer
arrow_outward
Polyline
Graphics Editor
arrow_outward
frame_source
Python Playground
arrow_outward
build
Tools
expand_more
restart_alt
Load "Hello Weaver!"
post_add
Generate Lorem ipsum...
code
Format HTML
code_blocks
Format CSS
data_object
Format JavaScript
library_add
Library
expand_more
A
Algolia JS
Animate CSS
Apex Charts JS
B
Bulma CSS
Bootstrap
C
Chart JS
Chartist
Create JS
D
D3
Dojo
F
Foundation
Fullpage JS
G
Granim JS
Google Charts
H
Halfmoon
J
jQuery
M
Materialize
Moment JS
Masonry JS
Milligram CSS
P
Pure CSS
Primer CSS
Popper JS
Pattern CSS
Picnic CSS
R
React JS
Raphael JS
Raisin CSS
S
Semantic UI
Skeleton CSS
Spectre CSS
Tachyons CSS
T
Tailwind
Three JS
U
UI Kit
Vis JS
W
Water CSS
download
Download
expand_more
developer_mode
Download as HTML
folder_zip
Download as .ZIP
cloud_upload
Fork
account_circle
Login
settings
Settings
expand_more
Editor font
14
px
A
A
Night mode
Line number
Mini map
Word wrap
sync_alt
Reset Settings
smart_display
Run
left_panel_close
AI Assistant
dashboard
All
HTML
CSS
JS
visibility
Preview
bolt
Live Preview
terminal
JS Console
<!DOCTYPE html> <html> <head> <title>Animated Slot Machine game</title> </head> <body> <!-- Start your code here --> <div class="casino-page"> <div class="ambient ambient-one"></div> <div class="ambient ambient-two"></div> <main class="slot-machine"> <!-- TOP MARQUEE --> <div class="marquee"> <div class="bulbs top-bulbs" id="topBulbs"></div> <div class="marquee-content"> <div class="crown">♛</div> <div> <div class="super">SUPER</div> <h1>LUCKY 7</h1> <div class="subtitle">DELUXE</div> </div> <div class="crown">♛</div> </div> <div class="jackpot"> <span>JACKPOT</span> <strong id="jackpotValue">10,000</strong> <small>CREDITS</small> </div> </div> <!-- MACHINE BODY --> <div class="machine-body"> <div class="side-light left-light"></div> <div class="side-light right-light"></div> <!-- PAYOUT STRIP --> <div class="payout-strip"> <div> <span class="mini-symbol seven">7</span> <span class="mini-symbol seven">7</span> <span class="mini-symbol seven">7</span> <strong>500×</strong> </div> <div> <span class="mini-symbol diamond">◆</span> <span class="mini-symbol diamond">◆</span> <span class="mini-symbol diamond">◆</span> <strong>100×</strong> </div> <div> <span class="mini-symbol bell">●</span> <span class="mini-symbol bell">●</span> <span class="mini-symbol bell">●</span> <strong>50×</strong> </div> <div> <span class="mini-symbol cherry">●</span> <span class="mini-symbol cherry">●</span> <span class="mini-symbol cherry">●</span> <strong>20×</strong> </div> </div> <!-- REELS --> <div class="reel-area"> <div class="payline"></div> <div class="payline-arrow left-arrow"> ▶ </div> <div class="payline-arrow right-arrow"> ◀ </div> <div class="reels"> <div class="reel-window"> <div class="reel" id="reel1"></div> <div class="reel-glass"></div> </div> <div class="reel-window"> <div class="reel" id="reel2"></div> <div class="reel-glass"></div> </div> <div class="reel-window"> <div class="reel" id="reel3"></div> <div class="reel-glass"></div> </div> </div> </div> <!-- MESSAGE --> <div class="message-panel"> <div class="message-icon"> ★ </div> <div> <span id="messageLabel"> READY TO PLAY </span> <strong id="messageText"> PRESS SPIN </strong> </div> <div class="message-icon"> ★ </div> </div> <!-- INFO DISPLAYS --> <div class="display-row"> <div class="digital-display"> <span>CREDITS</span> <strong id="credits"> 1,000 </strong> </div> <div class="digital-display"> <span>BET</span> <strong id="bet"> 10 </strong> </div> <div class="digital-display"> <span>WIN</span> <strong id="win"> 0 </strong> </div> </div> <!-- BUTTONS --> <div class="control-panel"> <button id="betDown" class="control-btn small-btn" title="Decrease bet" > − <span>BET</span> </button> <button id="betUp" class="control-btn small-btn" title="Increase bet" > + <span>BET</span> </button> <button id="maxBet" class="control-btn max-btn" > MAX <span>BET</span> </button> <button id="spinButton" class="spin-button" > <span class="spin-inner"> SPIN <small>GOOD LUCK!</small> </span> </button> <button id="soundButton" class="control-btn sound-btn" title="Toggle sound" > ♪ <span>SOUND</span> </button> </div> <div class="bottom-label"> <span>★</span> 3 REELS • 1 PAYLINE • FICTIONAL CREDITS <span>★</span> </div> </div> <!-- LEVER --> <div class="lever" id="lever"> <div class="lever-slot"></div> <div class="lever-arm"> <div class="lever-stick"></div> <button class="lever-ball" id="leverBall" aria-label="Pull slot machine lever" ></button> </div> </div> </main> <div class="instructions"> <span>SPACE</span> Spin <i></i> <span>↑ ↓</span> Change Bet <i></i> Pull the lever or press SPIN </div> </div> <!-- End your code here --> </body> </html>
* { box-sizing: border-box; } :root { --gold: #ffd45c; --gold-dark: #b36a08; --red: #d81832; --red-dark: #6c0614; --cream: #fff5cf; --black: #08080a; } html, body { margin: 0; min-height: 100%; } body { min-height: 100vh; overflow-x: hidden; background: radial-gradient( circle at 50% 25%, #401225 0, #170812 38%, #060609 75% ); color: white; font-family: Arial, Helvetica, sans-serif; } /* ========================================= PAGE ========================================= */ .casino-page { position: relative; min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 45px 20px 30px; overflow: hidden; } .casino-page::before { content: ""; position: absolute; inset: 0; opacity: .11; pointer-events: none; background-image: radial-gradient( circle, white 1px, transparent 1px ); background-size: 35px 35px; } /* ambient glow */ .ambient { position: absolute; width: 500px; height: 500px; border-radius: 50%; filter: blur(130px); pointer-events: none; } .ambient-one { left: -180px; top: 15%; background: rgba(207,24,52,.18); } .ambient-two { right: -180px; bottom: 5%; background: rgba(255,179,42,.12); } /* ========================================= MACHINE ========================================= */ .slot-machine { position: relative; z-index: 2; width: min(620px, 92vw); filter: drop-shadow( 0 40px 45px rgba(0,0,0,.7) ); } /* ========================================= MARQUEE ========================================= */ .marquee { position: relative; min-height: 205px; padding: 25px 25px 17px; border: 5px solid #8a4c08; border-bottom: 3px solid #4d2705; border-radius: 42px 42px 14px 14px; background: linear-gradient( 145deg, #6f0817, #c2172f 45%, #740916 ); box-shadow: inset 0 0 0 3px #f6b53a, inset 0 0 35px rgba(255,182,36,.28), 0 -4px 0 #3d1e05; } /* marquee inner panel */ .marquee::after { content: ""; position: absolute; inset: 19px 21px 15px; z-index: 0; border: 2px solid rgba(255,211,91,.7); border-radius: 25px 25px 9px 9px; box-shadow: inset 0 0 20px rgba(0,0,0,.25); pointer-events: none; } /* bulbs */ .bulbs { position: absolute; left: 25px; right: 25px; top: 7px; z-index: 5; display: flex; justify-content: space-between; } .bulb { width: 9px; height: 9px; border-radius: 50%; background: #fff3a1; box-shadow: 0 0 5px #fff, 0 0 12px #ffd43b, 0 0 20px #ff8a00; animation: bulbPulse .8s infinite alternate; } .bulb:nth-child(even) { animation-delay: -.4s; } @keyframes bulbPulse { from { opacity: .3; transform: scale(.75); } to { opacity: 1; transform: scale(1.1); } } /* marquee text */ .marquee-content { position: relative; z-index: 2; display: flex; align-items: center; justify-content: center; gap: 25px; text-align: center; } .crown { color: var(--gold); font-size: 35px; text-shadow: 0 2px 0 #7d4405, 0 0 15px rgba(255,205,70,.6); } .super { margin-bottom: -5px; color: #fff1aa; font-family: Georgia, serif; font-size: 15px; font-style: italic; letter-spacing: 5px; } .marquee h1 { margin: 0; color: #ffd85d; font-family: Georgia, "Times New Roman", serif; font-size: clamp(39px, 8vw, 61px); line-height: .95; letter-spacing: -3px; text-shadow: 0 2px 0 #fff3b4, 0 5px 0 #9b5205, 0 8px 10px rgba(0,0,0,.55), 0 0 25px rgba(255,185,29,.45); } .subtitle { margin-top: 7px; color: #ffe89a; font-size: 8px; font-weight: bold; letter-spacing: 7px; } /* jackpot */ .jackpot { position: relative; z-index: 2; width: 210px; margin: 13px auto 0; display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 8px; padding: 6px 11px; border: 1px solid #efb53e; border-radius: 6px; background: rgba(42,3,10,.72); box-shadow: inset 0 0 12px rgba(0,0,0,.5); } .jackpot span, .jackpot small { color: #c89843; font-size: 6px; letter-spacing: 1px; } .jackpot strong { color: #ffe579; font-family: "Courier New", monospace; font-size: 16px; text-align: center; text-shadow: 0 0 10px rgba(255,213,75,.5); } /* ========================================= BODY ========================================= */ .machine-body { position: relative; padding: 17px 28px 23px; border: 5px solid #6b3908; border-top: 0; border-radius: 4px 4px 28px 28px; background: linear-gradient( 90deg, #8d4e0b 0%, #e4a632 4%, #ffd26b 7%, #9c590d 11%, #b10d25 14%, #8d091b 50%, #b10d25 86%, #9c590d 89%, #ffd26b 93%, #e4a632 96%, #8d4e0b 100% ); box-shadow: inset 0 -15px 25px rgba(0,0,0,.32); } /* inner red body */ .machine-body::before { content: ""; position: absolute; inset: 9px 16px 13px; border-radius: 6px 6px 18px 18px; background: linear-gradient( #82091b, #b00d27 40%, #750616 ); box-shadow: inset 0 0 30px rgba(0,0,0,.35); pointer-events: none; } /* side light strips */ .side-light { position: absolute; top: 30px; bottom: 30px; z-index: 5; width: 7px; border-radius: 10px; background: repeating-linear-gradient( to bottom, #fff0a3 0 7px, #d87c11 7px 14px ); box-shadow: 0 0 10px rgba(255,197,58,.55); } .left-light { left: 11px; } .right-light { right: 11px; } /* ========================================= PAYOUT STRIP ========================================= */ .payout-strip { position: relative; z-index: 3; display: grid; grid-template-columns: repeat(4, 1fr); gap: 3px; padding: 7px; border: 2px solid #573007; border-radius: 8px 8px 3px 3px; background: linear-gradient( #f8d378, #b8791a ); box-shadow: inset 0 2px 3px rgba(255,255,255,.5), 0 4px 7px rgba(0,0,0,.3); } .payout-strip > div { min-width: 0; display: flex; align-items: center; justify-content: center; gap: 2px; padding: 4px; border-right: 1px solid rgba(84,44,2,.25); color: #5e2605; font-size: 8px; } .payout-strip > div:last-child { border-right: 0; } .payout-strip strong { margin-left: 3px; font-size: 7px; } .mini-symbol { font-size: 12px; font-weight: bold; } .mini-symbol.seven { color: #d70d25; } .mini-symbol.diamond { color: #297cd7; } .mini-symbol.bell { color: #e29b08; } .mini-symbol.cherry { color: #cc1730; } /* ========================================= REEL AREA ========================================= */ .reel-area { position: relative; z-index: 3; margin-top: 9px; padding: 13px 17px; border: 4px solid #4e2904; border-radius: 8px; background: linear-gradient( #251509, #6e3e0b 8%, #d9a247 12%, #9b681f 88%, #392006 ); box-shadow: inset 0 0 0 2px #f0c363, inset 0 8px 15px rgba(0,0,0,.5), 0 7px 15px rgba(0,0,0,.4); } .reels { position: relative; z-index: 2; display: grid; grid-template-columns: repeat(3, 1fr); gap: 9px; } .reel-window { position: relative; height: 190px; overflow: hidden; border: 3px solid #352312; border-radius: 7px; background: #fff8e5; box-shadow: inset 0 12px 20px rgba(0,0,0,.18), inset 0 -12px 20px rgba(0,0,0,.18); } /* reel itself */ .reel { position: absolute; left: 0; top: -190px; width: 100%; will-change: transform; } .symbol { height: 95px; display: flex; align-items: center; justify-content: center; border-bottom: 1px solid rgba(88,63,30,.12); background: linear-gradient( 90deg, #e9dfc8, #fffef5 15%, #ffffff 50%, #fffef5 85%, #e9dfc8 ); font-size: 56px; font-weight: bold; line-height: 1; text-shadow: 0 3px 3px rgba(0,0,0,.15); } /* individual symbols */ .symbol[data-symbol="7"] { color: #e1162f; font-family: Georgia, serif; font-size: 66px; font-style: italic; text-shadow: 2px 3px 0 #7f0616, 0 4px 5px rgba(0,0,0,.25); } .symbol[data-symbol="BAR"] { color: white; font-size: 25px; letter-spacing: -1px; } .symbol[data-symbol="BAR"] span { padding: 7px 9px; border-radius: 3px; background: linear-gradient( #303030, #050505 ); box-shadow: inset 0 0 0 2px #555, 0 3px 4px rgba(0,0,0,.3); } .symbol[data-symbol="DIAMOND"] { color: #3da4ff; font-size: 55px; text-shadow: 0 0 2px white, 0 3px 4px rgba(0,0,0,.25); } .symbol[data-symbol="CHERRY"] { font-size: 50px; } .symbol[data-symbol="BELL"] { font-size: 52px; } .symbol[data-symbol="LEMON"] { font-size: 49px; } /* reel glass */ .reel-glass { position: absolute; inset: 0; z-index: 4; pointer-events: none; background: linear-gradient( to bottom, rgba(0,0,0,.18), transparent 25%, rgba(255,255,255,.08) 48%, transparent 72%, rgba(0,0,0,.2) ); box-shadow: inset 0 0 16px rgba(0,0,0,.28); } /* payline */ .payline { position: absolute; left: 5px; right: 5px; top: 50%; z-index: 7; height: 2px; transform: translateY(-50%); background: rgba(255,31,62,.75); box-shadow: 0 0 5px rgba(255,24,55,.7); pointer-events: none; } .payline-arrow { position: absolute; top: 50%; z-index: 8; transform: translateY(-50%); color: #ffdf64; font-size: 13px; text-shadow: 0 0 6px #ff2c36; } .left-arrow { left: 1px; } .right-arrow { right: 1px; } /* spinning blur */ .reel.spinning .symbol { filter: blur(1.7px); opacity: .85; } /* winning symbols */ .symbol.winner { position: relative; z-index: 10; animation: winningSymbol .55s infinite alternate; } @keyframes winningSymbol { from { transform: scale(1); filter: brightness(1); } to { transform: scale(1.12); filter: brightness(1.25) drop-shadow( 0 0 8px #ffd53d ); } } /* ========================================= MESSAGE PANEL ========================================= */ .message-panel { position: relative; z-index: 3; min-height: 52px; margin-top: 10px; display: flex; align-items: center; justify-content: center; gap: 16px; padding: 8px; border: 2px solid #d09a31; border-radius: 7px; background: linear-gradient( #32060d, #170206 ); text-align: center; box-shadow: inset 0 0 12px black; } .message-panel span { display: block; color: #a46e2c; font-size: 6px; letter-spacing: 2px; } .message-panel strong { display: block; margin-top: 3px; color: #ffd95d; font-family: "Courier New", monospace; font-size: 14px; letter-spacing: 2px; text-shadow: 0 0 9px rgba(255,198,51,.55); } .message-icon { color: #9c6a21; font-size: 12px; } /* jackpot animation */ .slot-machine.big-win .message-panel { animation: winPanel .35s infinite alternate; } @keyframes winPanel { from { box-shadow: inset 0 0 12px black; } to { box-shadow: inset 0 0 18px #9b3100, 0 0 20px rgba(255,197,52,.6); } } /* ========================================= DISPLAYS ========================================= */ .display-row { position: relative; z-index: 3; display: grid; grid-template-columns: repeat(3, 1fr); gap: 7px; margin-top: 9px; } .digital-display { padding: 7px 10px; border: 1px solid #cf9a31; border-radius: 5px; background: #170a05; box-shadow: inset 0 0 8px black; text-align: center; } .digital-display span { display: block; color: #936c35; font-size: 6px; letter-spacing: 1px; } .digital-display strong { display: block; margin-top: 3px; color: #ffcf49; font-family: "Courier New", monospace; font-size: 17px; font-weight: normal; text-shadow: 0 0 8px rgba(255,195,47,.45); } /* ========================================= CONTROL PANEL ========================================= */ .control-panel { position: relative; z-index: 3; display: flex; align-items: center; justify-content: center; gap: 8px; margin-top: 13px; padding: 12px 10px; border: 2px solid #663a08; border-radius: 8px; background: linear-gradient( #d39a32, #80500f ); box-shadow: inset 0 2px 3px rgba(255,255,255,.35), inset 0 -3px 5px rgba(0,0,0,.35); } .control-btn { height: 49px; min-width: 50px; border: 2px solid #512606; border-radius: 50%; background: radial-gradient( circle at 40% 30%, #ffe381, #dc8e14 55%, #844100 ); color: #562400; font-size: 17px; font-weight: bold; box-shadow: inset 0 2px 2px rgba(255,255,255,.55), 0 4px 0 #5e3207, 0 6px 6px rgba(0,0,0,.35); cursor: pointer; transition: transform .08s, box-shadow .08s; } .control-btn span { display: block; font-size: 5px; letter-spacing: .5px; } .control-btn:active { transform: translateY(3px); box-shadow: inset 0 2px 2px rgba(255,255,255,.4), 0 1px 0 #5e3207; } .max-btn { font-size: 10px; } .sound-btn { font-size: 17px; } /* spin */ .spin-button { width: 102px; height: 72px; padding: 5px; border: 3px solid #5b1905; border-radius: 50%; background: linear-gradient( #ffca48, #a55c09 ); box-shadow: 0 6px 0 #542502, 0 9px 12px rgba(0,0,0,.4); cursor: pointer; transition: transform .08s, box-shadow .08s; } .spin-inner { width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; border-radius: 50%; background: radial-gradient( circle at 40% 28%, #ff5a5f, #d4112b 45%, #7e0617 80% ); color: #fff6d3; font-size: 19px; font-weight: 900; text-shadow: 0 2px 2px rgba(0,0,0,.4); box-shadow: inset 0 3px 4px rgba(255,255,255,.3), inset 0 -5px 8px rgba(0,0,0,.3); } .spin-inner small { margin-top: 2px; font-size: 5px; letter-spacing: .8px; } .spin-button:active { transform: translateY(4px); box-shadow: 0 2px 0 #542502, 0 4px 5px rgba(0,0,0,.4); } .spin-button:disabled { cursor: default; filter: grayscale(.3); opacity: .7; } /* ========================================= BOTTOM ========================================= */ .bottom-label { position: relative; z-index: 3; margin-top: 12px; color: #e0a941; font-size: 6px; text-align: center; letter-spacing: 1.2px; } .bottom-label span { margin: 0 6px; } /* ========================================= LEVER ========================================= */ .lever { position: absolute; right: -57px; top: 310px; z-index: -1; width: 72px; height: 195px; } .lever-slot { position: absolute; left: 12px; top: 40px; width: 50px; height: 105px; border: 5px solid #593006; border-radius: 12px; background: linear-gradient( 90deg, #8e550f, #e1ae4c, #724007 ); box-shadow: inset 0 0 8px rgba(0,0,0,.5); } .lever-arm { position: absolute; left: 31px; top: 0; width: 30px; height: 125px; transform-origin: 15px 112px; transition: transform .25s ease-in; } .lever-stick { position: absolute; left: 11px; top: 24px; width: 9px; height: 100px; border-radius: 8px; background: linear-gradient( 90deg, #686868, #eeeeee 45%, #737373 ); box-shadow: 2px 3px 5px rgba(0,0,0,.45); } .lever-ball { position: absolute; left: -1px; top: 0; width: 33px; height: 33px; border: 2px solid #710513; border-radius: 50%; background: radial-gradient( circle at 35% 25%, #ff8a8d, #e41c34 35%, #8c0617 75% ); box-shadow: inset 4px 4px 6px rgba(255,255,255,.25), inset -5px -6px 7px rgba(0,0,0,.3), 0 4px 7px rgba(0,0,0,.5); cursor: pointer; } .lever.pulled .lever-arm { transform: rotate(125deg); } /* ========================================= INSTRUCTIONS ========================================= */ .instructions { position: relative; z-index: 2; margin-top: 25px; display: flex; align-items: center; gap: 9px; color: #776c6e; font-size: 8px; } .instructions span { padding: 4px 6px; border: 1px solid #4c4144; border-radius: 4px; background: #171316; color: #a99b9e; font-size: 7px; } .instructions i { width: 1px; height: 12px; background: #3d3336; } /* ========================================= WIN FLASH ========================================= */ .slot-machine.jackpot-win .marquee { animation: jackpotFlash .25s infinite alternate; } @keyframes jackpotFlash { from { filter: brightness(1); } to { filter: brightness(1.4) saturate(1.3); } } /* ========================================= RESPONSIVE ========================================= */ @media (max-width: 720px) { .slot-machine { width: min(560px, 88vw); } .lever { right: -49px; } .symbol { font-size: 46px; } } @media (max-width: 540px) { .casino-page { padding: 25px 8px; } .slot-machine { width: 94vw; } .machine-body { padding: 14px 22px 20px; } .marquee-content { gap: 10px; } .crown { font-size: 25px; } .reel-window { height: 160px; } .symbol { height: 80px; font-size: 38px; } .symbol[data-symbol="7"] { font-size: 53px; } .reel { top: -160px; } .lever { display: none; } .control-panel { gap: 5px; } .control-btn { min-width: 42px; width: 42px; height: 42px; } .spin-button { width: 84px; height: 63px; } .spin-inner { font-size: 16px; } .instructions { display: none; } }
/* ========================================= SLOT MACHINE ========================================= */ /* Symbols used on the reels. These are intentionally repeated with different frequency. Rare symbols such as 7 appear less often. */ const symbols = [ "CHERRY", "LEMON", "BAR", "BELL", "CHERRY", "DIAMOND", "LEMON", "BAR", "CHERRY", "BELL", "DIAMOND", "7" ]; const symbolDisplay = { "CHERRY": "?", "LEMON": "?", "BELL": "?", "DIAMOND": "◆", "BAR": "BAR", "7": "7" }; /* ========================================= GAME STATE ========================================= */ let credits = 1000; let bet = 10; let win = 0; let spinning = false; let soundEnabled = true; /* ========================================= ELEMENTS ========================================= */ const machine = document.querySelector( ".slot-machine" ); const reelElements = [ document.getElementById( "reel1" ), document.getElementById( "reel2" ), document.getElementById( "reel3" ) ]; const creditsElement = document.getElementById( "credits" ); const betElement = document.getElementById( "bet" ); const winElement = document.getElementById( "win" ); const spinButton = document.getElementById( "spinButton" ); const betDown = document.getElementById( "betDown" ); const betUp = document.getElementById( "betUp" ); const maxBet = document.getElementById( "maxBet" ); const soundButton = document.getElementById( "soundButton" ); const lever = document.getElementById( "lever" ); const leverBall = document.getElementById( "leverBall" ); const messageLabel = document.getElementById( "messageLabel" ); const messageText = document.getElementById( "messageText" ); const jackpotValue = document.getElementById( "jackpotValue" ); /* ========================================= CREATE MARQUEE BULBS ========================================= */ const bulbContainer = document.getElementById( "topBulbs" ); for ( let i = 0; i < 22; i++ ) { const bulb = document.createElement( "span" ); bulb.className = "bulb"; bulb.style.animationDelay = ( i * .07 ) + "s"; bulbContainer.appendChild( bulb ); } /* ========================================= BUILD REELS ========================================= */ /* We repeat the symbol array several times. This gives each reel enough content to move rapidly during the animation. */ function buildReel( reel ) { reel.innerHTML = ""; for ( let repeat = 0; repeat < 8; repeat++ ) { symbols.forEach( function(symbol) { const item = document.createElement( "div" ); item.className = "symbol"; item.dataset.symbol = symbol; if ( symbol === "BAR" ) { item.innerHTML = "<span>BAR</span>"; } else { item.textContent = symbolDisplay[ symbol ]; } reel.appendChild( item ); } ); } } reelElements.forEach( buildReel ); /* ========================================= INITIAL REEL POSITIONS ========================================= */ const initialSymbols = [ "CHERRY", "7", "BELL" ]; function showInitialSymbols() { reelElements.forEach( function(reel, index) { renderStaticReel( reel, initialSymbols[index] ); } ); } function renderStaticReel( reel, centerSymbol ) { /* Display 3 symbols: one above, center winner, one below. */ const centerIndex = symbols.indexOf( centerSymbol ); const previous = symbols[ ( centerIndex - 1 + symbols.length ) % symbols.length ]; const next = symbols[ ( centerIndex + 1 ) % symbols.length ]; reel.innerHTML = ""; [ previous, centerSymbol, next ].forEach( function(symbol) { const item = document.createElement( "div" ); item.className = "symbol"; item.dataset.symbol = symbol; if ( symbol === "BAR" ) { item.innerHTML = "<span>BAR</span>"; } else { item.textContent = symbolDisplay[ symbol ]; } reel.appendChild( item ); } ); reel.style.top = getReelTop(); reel.style.transform = "translateY(0)"; } function getSymbolHeight() { return ( window.innerWidth <= 540 ? 80 : 95 ); } function getReelTop() { return ( -getSymbolHeight() ) + "px"; } showInitialSymbols(); /* ========================================= RANDOM RESULT ========================================= */ function randomSymbol() { return symbols[ Math.floor( Math.random() * symbols.length ) ]; } function generateResult() { return [ randomSymbol(), randomSymbol(), randomSymbol() ]; } /* ========================================= PAYOUTS ========================================= */ function calculateWin( result ) { const [ a, b, c ] = result; /* Three matching symbols */ if ( a === b && b === c ) { switch (a) { case "7": return { multiplier: 500, message: "JACKPOT!", jackpot: true }; case "DIAMOND": return { multiplier: 100, message: "DIAMOND WIN!", jackpot: false }; case "BELL": return { multiplier: 50, message: "BELLS!", jackpot: false }; case "BAR": return { multiplier: 30, message: "TRIPLE BAR!", jackpot: false }; case "CHERRY": return { multiplier: 20, message: "CHERRY WIN!", jackpot: false }; case "LEMON": return { multiplier: 10, message: "LEMON WIN!", jackpot: false }; } } /* Any two matching symbols */ if ( a === b || b === c || a === c ) { return { multiplier: 2, message: "PAIR!", jackpot: false }; } /* Any cherry provides a small payout */ if ( result.includes( "CHERRY" ) ) { return { multiplier: 1, message: "CHERRY!", jackpot: false }; } return { multiplier: 0, message: "TRY AGAIN", jackpot: false }; } /* ========================================= SPIN ANIMATION ========================================= */ function spin() { if ( spinning ) { return; } if ( credits < bet ) { setMessage( "NOT ENOUGH CREDITS", "LOW BALANCE" ); playErrorSound(); return; } spinning = true; credits -= bet; win = 0; updateDisplays(); machine.classList.remove( "big-win", "jackpot-win" ); clearWinningSymbols(); spinButton.disabled = true; setMessage( "SPINNING", "GOOD LUCK!" ); pullLeverAnimation(); playSpinSound(); const result = generateResult(); /* Create fast-scrolling content on each reel. */ reelElements.forEach( function(reel) { buildReel( reel ); reel.classList.add( "spinning" ); reel.style.top = "0px"; reel.style.transition = "none"; reel.style.transform = "translateY(0)"; } ); /* Start each reel moving. */ requestAnimationFrame( function() { reelElements.forEach( function( reel, index ) { const distance = -( 50 + index * 8 ) * getSymbolHeight(); reel.style.transition = "transform " + ( 1.25 + index * .45 ) + "s cubic-bezier(.12,.65,.18,1)"; reel.style.transform = "translateY(" + distance + "px)"; } ); } ); /* Stop reels one at a time. */ result.forEach( function( symbol, index ) { const delay = 1250 + index * 450; setTimeout( function() { reelElements[index] .classList.remove( "spinning" ); renderStaticReel( reelElements[index], symbol ); playReelStopSound( index ); /* After final reel stops, calculate payout. */ if ( index === 2 ) { setTimeout( function() { finishSpin( result ); }, 220 ); } }, delay ); } ); } /* ========================================= FINISH SPIN ========================================= */ function finishSpin( result ) { const payout = calculateWin( result ); win = bet * payout.multiplier; credits += win; updateDisplays(); if ( win > 0 ) { setMessage( payout.message, "+" + formatNumber( win ) + " CREDITS" ); highlightWinningSymbols( result ); machine.classList.add( "big-win" ); if ( payout.jackpot ) { machine.classList.add( "jackpot-win" ); playJackpotSound(); } else { playWinSound(); } animateCredits(); } else { setMessage( "NO WIN", "SPIN AGAIN" ); playLoseSound(); } /* Slowly increase displayed jackpot. */ const currentJackpot = Number( jackpotValue.textContent .replace( /,/g, "" ) ); jackpotValue.textContent = formatNumber( currentJackpot + bet ); spinning = false; spinButton.disabled = false; } /* ========================================= WIN HIGHLIGHT ========================================= */ function highlightWinningSymbols( result ) { const allSame = result[0] === result[1] && result[1] === result[2]; reelElements.forEach( function( reel, index ) { /* Middle child is the payline symbol. */ const center = reel.children[1]; if (!center) { return; } if ( allSame ) { center.classList.add( "winner" ); return; } const symbol = result[index]; const matchesAnother = result.some( function( other, otherIndex ) { return ( otherIndex !== index && other === symbol ); } ); if ( matchesAnother || symbol === "CHERRY" ) { center.classList.add( "winner" ); } } ); } function clearWinningSymbols() { document .querySelectorAll( ".winner" ) .forEach( function(element) { element.classList.remove( "winner" ); } ); } /* ========================================= BET CONTROLS ========================================= */ const betLevels = [ 5, 10, 25, 50, 100 ]; function changeBet( direction ) { if ( spinning ) { return; } let index = betLevels.indexOf( bet ); index += direction; index = Math.max( 0, Math.min( betLevels.length - 1, index ) ); bet = betLevels[index]; updateDisplays(); playButtonSound(); } betDown.addEventListener( "click", function() { changeBet( -1 ); } ); betUp.addEventListener( "click", function() { changeBet( 1 ); } ); maxBet.addEventListener( "click", function() { if ( spinning ) { return; } bet = betLevels[ betLevels.length - 1 ]; updateDisplays(); playButtonSound(); } ); /* ========================================= SPIN BUTTON ========================================= */ spinButton.addEventListener( "click", spin ); /* ========================================= LEVER ========================================= */ leverBall.addEventListener( "click", function() { spin(); } ); function pullLeverAnimation() { lever.classList.add( "pulled" ); setTimeout( function() { lever.classList.remove( "pulled" ); }, 420 ); } /* ========================================= KEYBOARD ========================================= */ window.addEventListener( "keydown", function(event) { if ( event.code === "Space" ) { event.preventDefault(); spin(); } if ( event.code === "ArrowUp" ) { event.preventDefault(); changeBet( 1 ); } if ( event.code === "ArrowDown" ) { event.preventDefault(); changeBet( -1 ); } } ); /* ========================================= UI ========================================= */ function updateDisplays() { creditsElement.textContent = formatNumber( credits ); betElement.textContent = formatNumber( bet ); winElement.textContent = formatNumber( win ); } function formatNumber( number ) { return Number( number ).toLocaleString( "en-US" ); } function setMessage( label, text ) { messageLabel.textContent = label; messageText.textContent = text; } /* ========================================= CREDIT COUNT ANIMATION ========================================= */ function animateCredits() { creditsElement.animate( [ { transform: "scale(1)", color: "#ffcf49" }, { transform: "scale(1.2)", color: "#ffffff" }, { transform: "scale(1)", color: "#ffcf49" } ], { duration: 500, iterations: 2 } ); } /* ========================================= WEB AUDIO ========================================= */ let audioContext = null; function getAudioContext() { if ( !audioContext ) { audioContext = new ( window.AudioContext || window.webkitAudioContext )(); } return audioContext; } function tone( frequency, duration, volume, type, delay ) { if ( !soundEnabled ) { return; } const ctx = getAudioContext(); const oscillator = ctx.createOscillator(); const gain = ctx.createGain(); const start = ctx.currentTime + ( delay || 0 ); oscillator.type = type || "sine"; oscillator.frequency .setValueAtTime( frequency, start ); gain.gain .setValueAtTime( volume || .04, start ); gain.gain .exponentialRampToValueAtTime( .001, start + duration ); oscillator.connect( gain ); gain.connect( ctx.destination ); oscillator.start( start ); oscillator.stop( start + duration ); } /* button */ function playButtonSound() { tone( 440, .08, .03, "sine" ); } /* spin */ function playSpinSound() { if ( !soundEnabled ) { return; } for ( let i = 0; i < 12; i++ ) { tone( 120 + i * 12, .06, .012, "square", i * .055 ); } } /* reel stop */ function playReelStopSound( index ) { tone( 210 + index * 45, .11, .055, "triangle" ); tone( 100, .08, .025, "sine", .02 ); } /* normal win */ function playWinSound() { const notes = [ 523, 659, 784, 1046 ]; notes.forEach( function( note, index ) { tone( note, .25, .04, "triangle", index * .11 ); } ); } /* jackpot */ function playJackpotSound() { const notes = [ 523, 659, 784, 1046, 1318, 1568, 2093 ]; notes.forEach( function( note, index ) { tone( note, .35, .055, "triangle", index * .1 ); } ); } /* lose */ function playLoseSound() { tone( 180, .18, .025, "sine" ); tone( 130, .22, .02, "sine", .12 ); } /* error */ function playErrorSound() { tone( 110, .15, .035, "square" ); } /* ========================================= SOUND TOGGLE ========================================= */ soundButton.addEventListener( "click", function() { soundEnabled = !soundEnabled; soundButton.innerHTML = soundEnabled ? "♪<span>SOUND</span>" : "×<span>MUTED</span>"; if ( soundEnabled ) { playButtonSound(); } } ); /* ========================================= RESIZE ========================================= */ window.addEventListener( "resize", function() { /* Re-render static reels so their 80/95px mobile sizing remains correctly aligned. */ if ( !spinning ) { reelElements.forEach( function(reel) { reel.style.top = getReelTop(); } ); } } ); /* ========================================= INITIAL UI ========================================= */ updateDisplays(); setMessage( "READY TO PLAY", "PRESS SPIN" );
terminal
JavaScript Console
Preview
Clear
close
›
Run