Chotto.app
Login
Sign Up
ES
App Name
kuyawa / slot
App Icon in SVG
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> <defs> <linearGradient id="bg" x1="0" y1="0" x2="1" y2="1"> <stop offset="0%" stop-color="#1a1a2e"/> <stop offset="100%" stop-color="#0a0a14"/> </linearGradient> <linearGradient id="gold" x1="0" y1="0" x2="0" y2="1"> <stop offset="0%" stop-color="#ffe066"/> <stop offset="40%" stop-color="#ffd700"/> <stop offset="100%" stop-color="#b8860b"/> </linearGradient> <linearGradient id="red7" x1="0" y1="0" x2="0" y2="1"> <stop offset="0%" stop-color="#ff4757"/> <stop offset="100%" stop-color="#c0392b"/> </linearGradient> <linearGradient id="goldH" x1="0" y1="0" x2="0" y2="1"> <stop offset="0%" stop-color="#ffd700"/> <stop offset="100%" stop-color="#9a7510"/> </linearGradient> <linearGradient id="reelBg" x1="0" y1="0" x2="0" y2="1"> <stop offset="0%" stop-color="#0f0f1e"/> <stop offset="100%" stop-color="#16162a"/> </linearGradient> <filter id="glow"> <feGaussianBlur stdDeviation="8" result="b"/> <feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge> </filter> <filter id="glowSm"> <feGaussianBlur stdDeviation="3" result="b"/> <feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge> </filter> <filter id="shadow"> <feDropShadow dx="0" dy="4" stdDeviation="6" flood-color="#000" flood-opacity=".5"/> </filter> <clipPath id="rc"><rect x="56" y="168" width="400" height="180" rx="18"/></clipPath> </defs> <!-- Background rounded square --> <rect width="512" height="512" rx="96" fill="url(#bg)"/> <rect width="512" height="512" rx="96" fill="none" stroke="#ffd700" stroke-width="2.5" opacity=".15"/> <!-- Subtle inner border --> <rect x="8" y="8" width="496" height="496" rx="88" fill="none" stroke="#fff" stroke-width=".5" opacity=".04"/> <!-- Top decorative bar with gold accent --> <rect x="120" y="42" width="272" height="3" rx="1.5" fill="url(#goldH)" opacity=".5"/> <!-- Title: LUCKY --> <text x="256" y="90" text-anchor="middle" font-family="Georgia,serif" font-size="38" font-weight="400" fill="#ffd700" opacity=".6" letter-spacing="16">LUCKY</text> <!-- Machine body --> <rect x="40" y="110" width="432" height="320" rx="28" fill="#12121e" stroke="#28283e" stroke-width="1.5" filter="url(#shadow)"/> <rect x="40" y="110" width="432" height="320" rx="28" fill="none" stroke="#ffd700" stroke-width=".5" opacity=".1"/> <!-- Reel frame --> <rect x="56" y="168" width="400" height="180" rx="18" fill="url(#reelBg)" stroke="#2a2a44" stroke-width="1.5"/> <!-- Reel dividers --> <line x1="189" y1="178" x2="189" y2="338" stroke="#2a2a44" stroke-width="1"/> <line x1="323" y1="178" x2="323" y2="338" stroke="#2a2a44" stroke-width="1"/> <!-- Payline highlight --> <rect x="56" y="243" width="400" height="34" rx="0" fill="#ffd700" opacity=".06" clip-path="url(#rc)"/> <line x1="56" y1="260" x2="456" y2="260" stroke="#ffd700" stroke-width="1.5" opacity=".35"/> <!-- Payline arrows --> <polygon points="48,260 62,252 62,268" fill="#ffd700" opacity=".7"/> <polygon points="464,260 450,252 450,268" fill="#ffd700" opacity=".7"/> <!-- Reel gradient overlays for depth --> <rect x="56" y="168" width="400" height="70" rx="18" fill="url(#reelBg)" opacity=".7" clip-path="url(#rc)"/> <rect x="56" y="290" width="400" height="58" rx="18" fill="url(#reelBg)" opacity=".7" clip-path="url(#rc)"/> <!-- Seven symbols on reels --> <!-- Reel 1 - Seven --> <g filter="url(#glowSm)"> <text x="122" y="280" text-anchor="middle" font-family="Georgia,serif" font-size="72" font-weight="900" fill="none" stroke="#ffd700" stroke-width="3">7</text> <text x="122" y="280" text-anchor="middle" font-family="Georgia,serif" font-size="72" font-weight="900" fill="url(#red7)">7</text> </g> <!-- Reel 2 - Seven --> <g filter="url(#glowSm)"> <text x="256" y="280" text-anchor="middle" font-family="Georgia,serif" font-size="72" font-weight="900" fill="none" stroke="#ffd700" stroke-width="3">7</text> <text x="256" y="280" text-anchor="middle" font-family="Georgia,serif" font-size="72" font-weight="900" fill="url(#red7)">7</text> </g> <!-- Reel 3 - Seven --> <g filter="url(#glowSm)"> <text x="390" y="280" text-anchor="middle" font-family="Georgia,serif" font-size="72" font-weight="900" fill="none" stroke="#ffd700" stroke-width="3">7</text> <text x="390" y="280" text-anchor="middle" font-family="Georgia,serif" font-size="72" font-weight="900" fill="url(#red7)">7</text> </g> <!-- Faint symbols above and below payline for realism --> <text x="122" y="216" text-anchor="middle" font-family="Georgia,serif" font-size="38" font-weight="900" fill="#ffd700" opacity=".08">7</text> <text x="256" y="216" text-anchor="middle" font-family="Georgia,serif" font-size="38" font-weight="900" fill="#ffd700" opacity=".08">7</text> <text x="390" y="216" text-anchor="middle" font-family="Georgia,serif" font-size="38" font-weight="900" fill="#ffd700" opacity=".08">7</text> <text x="122" y="340" text-anchor="middle" font-family="Georgia,serif" font-size="38" font-weight="900" fill="#ffd700" opacity=".08">7</text> <text x="256" y="340" text-anchor="middle" font-family="Georgia,serif" font-size="38" font-weight="900" fill="#ffd700" opacity=".08">7</text> <text x="390" y="340" text-anchor="middle" font-family="Georgia,serif" font-size="38" font-weight="900" fill="#ffd700" opacity=".08">7</text> <!-- SPIN button --> <rect x="180" y="370" width="152" height="42" rx="12" fill="url(#gold)" filter="url(#shadow)"/> <rect x="180" y="370" width="152" height="20" rx="12" fill="#fff" opacity=".15"/> <text x="256" y="398" text-anchor="middle" font-family="sans-serif" font-size="16" font-weight="900" fill="#1a1a2e" letter-spacing="5">SPIN</text> <!-- Bottom decorative bar --> <rect x="120" y="442" width="272" height="3" rx="1.5" fill="url(#goldH)" opacity=".35"/> <!-- 777 text below machine --> <text x="256" y="484" text-anchor="middle" font-family="Georgia,serif" font-size="44" font-weight="900" fill="url(#gold)" filter="url(#glow)" letter-spacing="10">777</text> <!-- Sparkle effects --> <!-- Top-left sparkle --> <g transform="translate(82,138)" opacity=".8"> <line x1="0" y1="-7" x2="0" y2="7" stroke="#ffd700" stroke-width="2" stroke-linecap="round"/> <line x1="-7" y1="0" x2="7" y2="0" stroke="#ffd700" stroke-width="2" stroke-linecap="round"/> <line x1="-4" y1="-4" x2="4" y2="4" stroke="#ffd700" stroke-width="1" stroke-linecap="round"/> <line x1="4" y1="-4" x2="-4" y2="4" stroke="#ffd700" stroke-width="1" stroke-linecap="round"/> </g> <!-- Top-right sparkle --> <g transform="translate(430,138)" opacity=".65"> <line x1="0" y1="-6" x2="0" y2="6" stroke="#ffd700" stroke-width="1.8" stroke-linecap="round"/> <line x1="-6" y1="0" x2="6" y2="0" stroke="#ffd700" stroke-width="1.8" stroke-linecap="round"/> <line x1="-3.5" y1="-3.5" x2="3.5" y2="3.5" stroke="#ffd700" stroke-width="1" stroke-linecap="round"/> <line x1="3.5" y1="-3.5" x2="-3.5" y2="3.5" stroke="#ffd700" stroke-width="1" stroke-linecap="round"/> </g> <!-- Small sparkle near bottom 777 --> <g transform="translate(160,476)" opacity=".5"> <line x1="0" y1="-4" x2="0" y2="4" stroke="#ffd700" stroke-width="1.2" stroke-linecap="round"/> <line x1="-4" y1="0" x2="4" y2="0" stroke="#ffd700" stroke-width="1.2" stroke-linecap="round"/> </g> <g transform="translate(356,472)" opacity=".45"> <line x1="0" y1="-5" x2="0" y2="5" stroke="#ffd700" stroke-width="1.4" stroke-linecap="round"/> <line x1="-5" y1="0" x2="5" y2="0" stroke="#ffd700" stroke-width="1.4" stroke-linecap="round"/> <line x1="-3" y1="-3" x2="3" y2="3" stroke="#ffd700" stroke-width=".8" stroke-linecap="round"/> <line x1="3" y1="-3" x2="-3" y2="3" stroke="#ffd700" stroke-width=".8" stroke-linecap="round"/> </g> <!-- Tiny dot sparkles --> <circle cx="98" cy="260" r="1.8" fill="#ffd700" opacity=".5"/> <circle cx="418" cy="255" r="1.5" fill="#ffd700" opacity=".4"/> <circle cx="256" cy="148" r="1.5" fill="#ffd700" opacity=".35"/> <circle cx="310" cy="460" r="1.2" fill="#ffd700" opacity=".3"/> <circle cx="200" cy="465" r="1" fill="#ffd700" opacity=".25"/> </svg>
HTML Content
<!DOCTYPE html> <html lang="en" data-theme="dark"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Lucky 777</title> <style> [data-theme="dark"]{ --bg:#08080f;--surface:#12121e;--card:#181828;--card2:#1e1e32; --border:#28283e;--fg:#e8e8f2;--fg2:#a8a8c0;--muted:#5c5c78; --accent:#d4a017;--accent-br:#ffd700;--accent-dim:#9a7510; --accent-bg:rgba(212,160,23,.08);--win:#00e676;--win-bg:rgba(0,230,118,.08); --danger:#ff3d57;--reel-bg:#0a0a16;--reel-sh:rgba(0,0,0,.6); --payline:rgba(212,160,23,.5);--m-sh:rgba(0,0,0,.5);--overlay:rgba(0,0,0,.75); } [data-theme="light"]{ --bg:#f0ece3;--surface:#f8f6f2;--card:#fff;--card2:#fff; --border:#d8d4cb;--fg:#1a1a2e;--fg2:#5a5a70;--muted:#8a8a9e; --accent:#b8860b;--accent-br:#daa520;--accent-dim:#8b6508; --accent-bg:rgba(184,134,11,.06);--win:#2e7d32;--win-bg:rgba(46,125,50,.06); --danger:#c62828;--reel-bg:#f5f3ee;--reel-sh:rgba(0,0,0,.12); --payline:rgba(184,134,11,.4);--m-sh:rgba(0,0,0,.08);--overlay:rgba(255,255,255,.8); } :root{--sym-h:100px} @media(max-width:480px){:root{--sym-h:80px}} *,*::before,*::after{margin:0;padding:0;box-sizing:border-box} body{ font-family:'Segoe UI',-apple-system,BlinkMacSystemFont,system-ui,sans-serif; background:var(--bg);color:var(--fg);min-height:100vh; display:flex;flex-direction:column;align-items:center; transition:background .5s,color .4s;overflow-x:hidden; } body::before{ content:'';position:fixed;inset:0;pointer-events:none;z-index:0; background:radial-gradient(ellipse 600px 400px at 25% 15%,var(--accent-bg),transparent), radial-gradient(ellipse 500px 500px at 75% 85%,var(--accent-bg),transparent); animation:bgF 15s ease-in-out infinite alternate; } @keyframes bgF{to{transform:translate(20px,-15px) scale(1.05)}} #fx{position:fixed;inset:0;width:100%;height:100%;pointer-events:none;z-index:100} .app{position:relative;z-index:1;width:100%;max-width:500px;padding:16px;display:flex;flex-direction:column;align-items:center;gap:20px} .top-bar{width:100%;display:flex;justify-content:space-between;align-items:center;padding:4px 0} .logo{display:flex;align-items:center;gap:10px} .logo-text{font-size:1.5rem;font-weight:900;letter-spacing:3px;color:var(--accent-br);text-shadow:0 0 20px rgba(255,215,0,.25)} #themeBtn{ width:40px;height:40px;border:1px solid var(--border);border-radius:10px; background:var(--surface);color:var(--fg2);cursor:pointer; display:flex;align-items:center;justify-content:center;transition:all .2s; } #themeBtn:hover{background:var(--card2);color:var(--accent);border-color:var(--accent-dim)} #themeBtn:focus-visible{outline:2px solid var(--accent);outline-offset:2px} .machine{ width:100%;background:var(--card);border:1.5px solid var(--border); border-radius:20px;padding:20px;display:flex;flex-direction:column; align-items:center;gap:16px;position:relative;overflow:hidden; box-shadow:0 0 0 1px var(--border),0 20px 60px var(--m-sh),0 4px 16px var(--m-sh); transition:background .4s,border-color .4s,box-shadow .4s; } .machine::before{content:'';position:absolute;top:0;left:20px;right:20px;height:1px;background:linear-gradient(90deg,transparent,rgba(255,255,255,.06),transparent)} .win-display{ width:100%;display:flex;justify-content:center;align-items:center;gap:12px; padding:10px 16px;background:var(--surface);border:1px solid var(--border); border-radius:10px;transition:all .4s; } .win-display.winning{background:var(--win-bg);border-color:var(--win);box-shadow:0 0 24px rgba(0,230,118,.12)} .win-display.jackpot{background:rgba(255,215,0,.1);border-color:var(--accent-br);box-shadow:0 0 30px rgba(255,215,0,.2);animation:jpPulse .6s ease-in-out 4} @keyframes jpPulse{0%,100%{transform:scale(1)}50%{transform:scale(1.03)}} .win-label{font-size:.7rem;font-weight:700;letter-spacing:3px;color:var(--muted);transition:color .4s} .win-display.winning .win-label{color:var(--win)} .win-display.jackpot .win-label{color:var(--accent-br)} .win-amount{font-size:1.6rem;font-weight:900;font-variant-numeric:tabular-nums;min-width:50px;text-align:right;transition:color .4s} .win-display.winning .win-amount{color:var(--win)} .win-display.jackpot .win-amount{color:var(--accent-br)} .reel-frame{ position:relative;width:100%;background:var(--reel-bg);border:2px solid var(--border); border-radius:14px;box-shadow:inset 0 4px 16px var(--reel-sh);overflow:hidden; transition:background .4s,border-color .4s; } .reel-window{display:flex;justify-content:center;gap:6px;padding:0 6px} .reel{width:110px;height:calc(var(--sym-h)*3);overflow:hidden;position:relative} .reel::before,.reel::after{content:'';position:absolute;left:0;right:0;height:40%;z-index:2;pointer-events:none} .reel::before{top:0;background:linear-gradient(to bottom,var(--reel-bg),transparent)} .reel::after{bottom:0;background:linear-gradient(to top,var(--reel-bg),transparent)} .reel-strip{display:flex;flex-direction:column;will-change:transform} .reel-strip.blur{filter:blur(1.5px)} .sym{width:110px;height:var(--sym-h);display:flex;align-items:center;justify-content:center;flex-shrink:0} .sym svg{width:72px;height:72px} .payline{position:absolute;top:50%;left:0;right:0;height:2px;background:var(--payline);transform:translateY(-50%);z-index:3;pointer-events:none;transition:all .3s} .payline.active{height:3px;background:var(--accent-br);box-shadow:0 0 12px rgba(255,215,0,.5)} .pl-arrow{position:absolute;top:50%;transform:translateY(-50%);z-index:4;pointer-events:none;width:0;height:0} .pl-arrow.l{left:-2px;border-top:8px solid transparent;border-bottom:8px solid transparent;border-left:10px solid var(--accent)} .pl-arrow.r{right:-2px;border-top:8px solid transparent;border-bottom:8px solid transparent;border-right:10px solid var(--accent)} .controls{width:100%;display:flex;align-items:center;justify-content:space-between;gap:10px} .bet-sec{display:flex;align-items:center;gap:8px} .btn-s{ width:38px;height:38px;border:1.5px solid var(--border);border-radius:8px; background:var(--surface);color:var(--fg);font-size:1.2rem;font-weight:700; cursor:pointer;display:flex;align-items:center;justify-content:center; transition:all .15s;user-select:none; } .btn-s:hover{background:var(--card2);border-color:var(--accent-dim);color:var(--accent)} .btn-s:active{transform:scale(.92)} .btn-s:focus-visible{outline:2px solid var(--accent);outline-offset:2px} .bet-disp{display:flex;flex-direction:column;align-items:center;min-width:56px} .bet-lbl{font-size:.58rem;font-weight:700;letter-spacing:2px;color:var(--muted)} .bet-val{font-size:1.15rem;font-weight:900;color:var(--accent);font-variant-numeric:tabular-nums} .spin-btn{ padding:14px 36px;background:linear-gradient(135deg,var(--accent),var(--accent-br)); color:#000;border:none;border-radius:12px;font-size:1.05rem;font-weight:900; letter-spacing:3px;cursor:pointer;transition:all .2s;position:relative;overflow:hidden; box-shadow:0 4px 20px rgba(212,160,23,.25); } .spin-btn::after{content:'';position:absolute;inset:0;background:linear-gradient(135deg,rgba(255,255,255,.2),transparent 50%);pointer-events:none} .spin-btn:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 6px 30px rgba(212,160,23,.4)} .spin-btn:active:not(:disabled){transform:translateY(1px);box-shadow:0 2px 10px rgba(212,160,23,.2)} .spin-btn:disabled{opacity:.35;cursor:not-allowed;transform:none!important} .spin-btn:focus-visible{outline:2px solid var(--accent-br);outline-offset:3px} .cred-sec{display:flex;flex-direction:column;align-items:flex-end;min-width:76px} .cred-lbl{font-size:.58rem;font-weight:700;letter-spacing:2px;color:var(--muted)} .cred-val{font-size:1.15rem;font-weight:900;font-variant-numeric:tabular-nums;transition:color .3s} .cred-val.low{color:var(--danger)} .paytable{width:100%;border:1.5px solid var(--border);border-radius:14px;overflow:hidden;background:var(--card);transition:background .4s,border-color .4s} .pt-toggle{ width:100%;padding:12px 16px;background:0;border:0;color:var(--fg2); font-size:.72rem;font-weight:700;letter-spacing:3px;cursor:pointer; display:flex;justify-content:space-between;align-items:center;transition:color .2s; } .pt-toggle:hover{color:var(--fg)} .pt-toggle .chv{transition:transform .3s} .pt-toggle.open .chv{transform:rotate(180deg)} .pt-content{max-height:0;overflow:hidden;transition:max-height .4s ease} .pt-content.open{max-height:500px} .pt-inner{padding:0 16px 16px;display:flex;flex-direction:column;gap:5px} .pt-row{display:flex;align-items:center;justify-content:space-between;padding:7px 10px;border-radius:8px;background:var(--surface);transition:background .2s} .pt-row:hover{background:var(--card2)} .pt-syms{display:flex;gap:4px;align-items:center} .pt-syms svg{width:30px;height:30px} .pt-wild{font-size:.65rem;color:var(--muted);font-weight:700;margin-left:2px} .pt-mult{font-size:.85rem;font-weight:800;color:var(--accent)} .pt-mult.jp{color:var(--accent-br);text-shadow:0 0 8px rgba(255,215,0,.3)} .go-overlay{ position:absolute;inset:0;background:var(--overlay);backdrop-filter:blur(6px); display:flex;flex-direction:column;align-items:center;justify-content:center; gap:16px;z-index:10;border-radius:20px;opacity:0;pointer-events:none;transition:opacity .4s; } .go-overlay.vis{opacity:1;pointer-events:auto} .go-title{font-size:1.7rem;font-weight:900;color:var(--danger);letter-spacing:3px} .go-sub{font-size:.85rem;color:var(--fg2)} .reset-btn{ padding:12px 32px;background:var(--accent);color:#000;border:0;border-radius:10px; font-size:.85rem;font-weight:800;letter-spacing:2px;cursor:pointer;transition:all .2s; } .reset-btn:hover{background:var(--accent-br);transform:translateY(-1px)} .toast-box{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);z-index:200;display:flex;flex-direction:column;align-items:center;gap:8px;pointer-events:none} .toast{ padding:10px 20px;background:var(--card2);border:1px solid var(--border);border-radius:10px; color:var(--fg);font-size:.82rem;font-weight:600;box-shadow:0 8px 30px var(--m-sh); animation:tIn .3s ease,tOut .3s ease 2s forwards;pointer-events:auto;white-space:nowrap; } .toast.err{border-color:var(--danger);color:var(--danger)} @keyframes tIn{from{opacity:0;transform:translateY(20px) scale(.95)}to{opacity:1;transform:translateY(0) scale(1)}} @keyframes tOut{from{opacity:1;transform:translateY(0) scale(1)}to{opacity:0;transform:translateY(-10px) scale(.95)}} @media(max-width:480px){ .app{padding:10px;gap:14px} .machine{padding:14px;border-radius:16px} .reel{width:86px}.sym{width:86px}.sym svg{width:56px;height:56px} .spin-btn{padding:12px 24px;font-size:.95rem;letter-spacing:2px} .win-amount{font-size:1.3rem} .logo-text{font-size:1.2rem;letter-spacing:2px} .pt-syms svg{width:24px;height:24px} } @media(prefers-reduced-motion:reduce){body::before,.win-display.jackpot{animation:none}} </style> </head> <body> <canvas id="fx"></canvas> <div class="app"> <header class="top-bar"> <div class="logo"> <span class="logo-text">LUCKY 777</span> </div> <button id="themeBtn" aria-label="Toggle theme"> <svg id="themeIcon" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"> <path d="M21 12.79A9 9 0 1111.21 3a7 7 0 009.79 9.79z"/> </svg> </button> </header> <main style="width:100%;display:flex;flex-direction:column;align-items:center;gap:20px"> <div class="machine" id="machine"> <div class="win-display" id="winDisp"> <span class="win-label">WIN</span> <span class="win-amount" id="winAmt">0</span> </div> <div class="reel-frame"> <div class="reel-window"> <div class="reel" id="reel0"><div class="reel-strip" id="strip0"></div></div> <div class="reel" id="reel1"><div class="reel-strip" id="strip1"></div></div> <div class="reel" id="reel2"><div class="reel-strip" id="strip2"></div></div> </div> <div class="payline" id="payline"></div> <div class="pl-arrow l"></div> <div class="pl-arrow r"></div> </div> <div class="controls"> <div class="bet-sec"> <button class="btn-s" id="betDn" aria-label="Decrease bet">−</button> <div class="bet-disp"> <span class="bet-lbl">BET</span> <span class="bet-val" id="betVal">25</span> </div> <button class="btn-s" id="betUp" aria-label="Increase bet">+</button> </div> <button class="spin-btn" id="spinBtn">SPIN</button> <div class="cred-sec"> <span class="cred-lbl">CREDITS</span> <span class="cred-val" id="credVal">1,000</span> </div> </div> <div class="go-overlay" id="goOverlay"> <div class="go-title">GAME OVER</div> <div class="go-sub">You've run out of credits</div> <button class="reset-btn" id="resetBtn">PLAY AGAIN</button> </div> </div> <section class="paytable"> <button class="pt-toggle" id="ptToggle"> <span>PAYTABLE</span> <svg class="chv" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg> </button> <div class="pt-content" id="ptContent"> <div class="pt-inner" id="ptInner"></div> </div> </section> </main> </div> <div class="toast-box" id="toasts"></div> <script> /* ===== SYMBOL DEFINITIONS ===== */ const SYMS = ['seven','diamond','bar','bell','cherry','lemon']; const WEIGHTS = {seven:1,diamond:2,bar:3,bell:4,cherry:5,lemon:5}; const TOTAL_W = Object.values(WEIGHTS).reduce((a,b)=>a+b,0); const BETS = [10,25,50,100,250]; /* Payout table: [sym, multiplier] for three-of-a-kind */ const PAYOUTS_3 = {seven:100,bar:50,diamond:30,bell:20,cherry:15,lemon:10}; const PAYOUT_2_SEVENS = 5; const PAYOUT_1_SEVEN = 1; /* ===== SVG GENERATORS ===== */ function svgSeven(){ return `<svg viewBox="0 0 100 100"><text x="50" y="78" text-anchor="middle" font-size="70" font-weight="900" fill="none" stroke="#ffd700" stroke-width="4" font-family="Georgia,serif">7</text><text x="50" y="78" text-anchor="middle" font-size="70" font-weight="900" fill="#e63946" font-family="Georgia,serif">7</text></svg>`; } function svgDiamond(){ return `<svg viewBox="0 0 100 100"><polygon points="50,10 86,42 50,90 14,42" fill="#00acc1" stroke="#b2ebf2" stroke-width="1.5"/><polygon points="50,10 68,42 50,90" fill="#00838f" opacity=".45"/><polygon points="50,10 32,42 14,42" fill="#4dd0e1" opacity=".35"/><line x1="14" y1="42" x2="86" y2="42" stroke="#b2ebf2" stroke-width=".8" opacity=".5"/><line x1="50" y1="10" x2="32" y2="42" stroke="#b2ebf2" stroke-width=".5" opacity=".3"/><line x1="50" y1="10" x2="68" y2="42" stroke="#b2ebf2" stroke-width=".5" opacity=".3"/></svg>`; } function svgBar(){ return `<svg viewBox="0 0 100 100"><rect x="8" y="30" width="84" height="40" rx="5" fill="#2d2d2d" stroke="#ffd700" stroke-width="2"/><rect x="10" y="32" width="80" height="2" fill="#555" rx="1"/><text x="50" y="57" text-anchor="middle" font-size="20" font-weight="900" fill="#ffd700" font-family="sans-serif" letter-spacing="4">BAR</text></svg>`; } function svgBell(){ return `<svg viewBox="0 0 100 100"><path d="M36,26 C36,18 42,12 50,12 C58,12 64,18 64,26 L70,56 L30,56 Z" fill="#ffc107" stroke="#f9a825" stroke-width="1.5"/><rect x="26" y="56" width="48" height="7" rx="3.5" fill="#f9a825"/><circle cx="50" cy="74" r="7" fill="#f9a825" stroke="#e65100" stroke-width="1"/><circle cx="50" cy="10" r="4" fill="#f9a825"/><path d="M42,24 Q50,20 58,24" fill="none" stroke="#fff8e1" stroke-width="1.5" opacity=".5"/></svg>`; } function svgCherry(){ return `<svg viewBox="0 0 100 100"><path d="M50,16 C50,16 47,34 33,48" fill="none" stroke="#388e3c" stroke-width="2.5" stroke-linecap="round"/><path d="M50,16 C50,16 53,34 67,48" fill="none" stroke="#388e3c" stroke-width="2.5" stroke-linecap="round"/><ellipse cx="50" cy="14" rx="9" ry="4.5" fill="#4caf50" transform="rotate(-8,50,14)"/><circle cx="33" cy="62" r="17" fill="#e53935"/><circle cx="67" cy="62" r="17" fill="#c62828"/><circle cx="27" cy="56" r="4.5" fill="#ef9a9a" opacity=".45"/><circle cx="61" cy="56" r="4.5" fill="#ef9a9a" opacity=".45"/></svg>`; } function svgLemon(){ return `<svg viewBox="0 0 100 100"><ellipse cx="50" cy="52" rx="35" ry="25" fill="#fdd835" stroke="#f9a825" stroke-width="1.8" transform="rotate(-15,50,52)"/><path d="M19,46 Q13,52 19,58" fill="none" stroke="#66bb6a" stroke-width="2.5" stroke-linecap="round"/><path d="M81,38 Q87,35 85,42" fill="none" stroke="#66bb6a" stroke-width="2" stroke-linecap="round"/><ellipse cx="40" cy="44" rx="11" ry="6" fill="#fff9c4" opacity=".35" transform="rotate(-15,40,44)"/></svg>`; } const SVG_FNS = {seven:svgSeven,diamond:svgDiamond,bar:svgBar,bell:svgBell,cherry:svgCherry,lemon:svgLemon}; function symSVG(s){ return (SVG_FNS[s]||svgLemon)(); } /* ===== GAME STATE ===== */ let credits = 1000; let betIdx = 1; let spinning = false; let results = ['lemon','lemon','lemon']; /* Track what's currently visible [top, center, bottom] for each reel */ let visible = [ [rSym(),rSym(),rSym()], [rSym(),rSym(),rSym()], [rSym(),rSym(),rSym()] ]; function rSym(){ let r = Math.random()*TOTAL_W; for(const [s,w] of Object.entries(WEIGHTS)){r-=w;if(r<=0)return s;} return 'lemon'; } /* ===== DOM REFS ===== */ const $ = id => document.getElementById(id); const strips = [$('strip0'),$('strip1'),$('strip2')]; const spinBtn = $('spinBtn'); const betValEl = $('betVal'); const credValEl = $('credVal'); const winDisp = $('winDisp'); const winAmtEl = $('winAmt'); const paylineEl = $('payline'); const goOverlay = $('goOverlay'); const toasts = $('toasts'); /* ===== HELPERS ===== */ function getSymH(){ return parseInt(getComputedStyle(document.documentElement).getPropertyValue('--sym-h')); } function fmtNum(n){ return n.toLocaleString(); } function toast(msg, err){ const t = document.createElement('div'); t.className = 'toast' + (err?' err':''); t.textContent = msg; toasts.appendChild(t); setTimeout(()=>t.remove(), 2500); } /* ===== RENDERING ===== */ function renderSymCell(s){ const d = document.createElement('div'); d.className = 'sym'; d.innerHTML = symSVG(s); return d; } function renderInitialReels(){ for(let i=0;i<3;i++){ strips[i].innerHTML = ''; strips[i].style.transition = 'none'; strips[i].style.transform = 'translateY(0)'; for(const s of visible[i]){ strips[i].appendChild(renderSymCell(s)); } } } function updateCredits(){ credValEl.textContent = fmtNum(credits); credValEl.classList.toggle('low', credits < BETS[betIdx]); } function updateBet(){ betValEl.textContent = fmtNum(BETS[betIdx]); credValEl.classList.toggle('low', credits < BETS[betIdx]); } function clearWin(){ winDisp.classList.remove('winning','jackpot'); winAmtEl.textContent = '0'; paylineEl.classList.remove('active'); } /* ===== REEL SPINNING ===== */ function buildStrip(currentVis, target, extraCount){ /* Strip: [currentTop, currentCenter, currentBottom, ...extraCount random, newTop, TARGET, newBottom] */ const cells = []; for(const s of currentVis) cells.push(renderSymCell(s)); for(let i=0;i<extraCount;i++) cells.push(renderSymCell(rSym())); cells.push(renderSymCell(rSym())); // new top cells.push(renderSymCell(target)); // center = payline cells.push(renderSymCell(rSym())); // new bottom return cells; } function spin(){ if(spinning) return; const bet = BETS[betIdx]; if(credits < bet){ toast('Not enough credits!', true); return; } spinning = true; spinBtn.disabled = true; clearWin(); credits -= bet; updateCredits(); /* Determine results */ results = [rSym(), rSym(), rSym()]; const symH = getSymH(); const extras = [15, 22, 29]; // extra symbols per reel for stagger const durations = [1.3, 1.9, 2.5]; // spin durations let stopped = 0; for(let i=0;i<3;i++){ const strip = strips[i]; const cells = buildStrip(visible[i], results[i], extras[i]); /* Clear and populate */ strip.innerHTML = ''; strip.style.transition = 'none'; strip.style.transform = 'translateY(0)'; for(const c of cells) strip.appendChild(c); /* Force reflow */ void strip.offsetHeight; /* Add blur and animate */ strip.classList.add('blur'); strip.dataset.targetY = (3 + extras[i]) * symH; strip.style.transition = `transform ${durations[i]}s cubic-bezier(0.12,0.8,0.18,1)`; strip.style.transform = `translateY(-${strip.dataset.targetY}px)`; /* Listen for stop */ (function(idx){ function onEnd(e){ if(e.propertyName !== 'transform') return; strip.removeEventListener('transitionend', onEnd); strip.classList.remove('blur'); /* Bounce effect */ const ty = parseFloat(strip.dataset.targetY); strip.style.transition = 'transform .08s ease-out'; strip.style.transform = `translateY(-${ty - 6}px)`; setTimeout(()=>{ strip.style.transition = 'transform .12s ease-in'; strip.style.transform = `translateY(-${ty}px)`; }, 80); stopped++; if(stopped === 3){ /* Update visible state */ for(let r=0;r<3;r++){ visible[r] = [rSym(), results[r], rSym()]; } setTimeout(()=>{ spinning = false; spinBtn.disabled = false; evaluateWin(bet); }, 180); } } strip.addEventListener('transitionend', onEnd); })(i); } } /* ===== WIN EVALUATION ===== */ function evaluateWin(bet){ const [a,b,c] = results; let mult = 0; let isJackpot = false; if(a===b && b===c){ mult = PAYOUTS_3[a] || 10; if(a==='seven') isJackpot = true; } else { const sevenCount = [a,b,c].filter(s=>s==='seven').length; if(sevenCount === 2) mult = PAYOUT_2_SEVENS; else if(sevenCount === 1) mult = PAYOUT_1_SEVEN; } if(mult > 0){ const winAmount = bet * mult; credits += winAmount; updateCredits(); /* Animate win display */ paylineEl.classList.add('active'); if(isJackpot){ winDisp.classList.add('jackpot'); } else { winDisp.classList.add('winning'); } /* Count-up animation */ animateCount(winAmtEl, 0, winAmount, 600); /* Particles */ const rect = $('machine').getBoundingClientRect(); const cx = rect.left + rect.width/2; const cy = rect.top + rect.height * 0.35; const count = isJackpot ? 120 : mult >= 10 ? 60 : 25; const colors = isJackpot ? ['#ffd700','#ffab00','#ff6d00','#fff','#e63946'] : ['#ffd700','#ffab00','#00e676','#fff']; particles.emit(cx, cy, count, {colors, spread: isJackpot?12:7, speed: isJackpot?14:9}); if(isJackpot) toast('JACKPOT!'); } } function animateCount(el, from, to, dur){ const start = performance.now(); function tick(now){ const t = Math.min((now-start)/dur, 1); const ease = 1 - Math.pow(1-t, 3); el.textContent = fmtNum(Math.round(from + (to-from)*ease)); if(t<1) requestAnimationFrame(tick); } requestAnimationFrame(tick); } /* ===== PARTICLE SYSTEM ===== */ const fxCanvas = $('fx'); const fxCtx = fxCanvas.getContext('2d'); let fxParticles = []; let fxRunning = false; function resizeFx(){ fxCanvas.width = window.innerWidth; fxCanvas.height = window.innerHeight; } window.addEventListener('resize', resizeFx); resizeFx(); const particles = { emit(x, y, count, opts={}){ const {colors=['#ffd700'], spread=8, speed=10, gravity=0.18} = opts; for(let i=0;i<count;i++){ const angle = Math.random()*Math.PI*2; const v = (Math.random()*0.7+0.3)*speed; fxParticles.push({ x, y, vx: Math.cos(angle)*v*(Math.random()*0.6+0.4), vy: Math.sin(angle)*v - Math.random()*4 - 2, life:1, decay: 0.008+Math.random()*0.015, size: 1.5+Math.random()*3.5, color: colors[Math.floor(Math.random()*colors.length)], gravity, rotation: Math.random()*360, rotSpeed: (Math.random()-0.5)*10 }); } if(!fxRunning) fxLoop(); } }; function fxLoop(){ fxRunning = true; fxCtx.clearRect(0,0,fxCanvas.width,fxCanvas.height); fxParticles = fxParticles.filter(p=>p.life>0); for(const p of fxParticles){ p.x += p.vx; p.y += p.vy; p.vy += p.gravity; p.vx *= 0.99; p.life -= p.decay; p.rotation += p.rotSpeed; fxCtx.save(); fxCtx.globalAlpha = Math.max(0,p.life); fxCtx.translate(p.x, p.y); fxCtx.rotate(p.rotation*Math.PI/180); fxCtx.fillStyle = p.color; /* Draw small diamond shape instead of circle for sparkle */ const s = p.size; fxCtx.beginPath(); fxCtx.moveTo(0,-s); fxCtx.lineTo(s*0.6,0); fxCtx.moveTo(0,-s); fxCtx.lineTo(-s*0.6,0); fxCtx.lineTo(0,s); fxCtx.lineTo(s*0.6,0); fxCtx.closePath(); fxCtx.fill(); /* Also draw a small circle for glow */ fxCtx.globalAlpha = Math.max(0,p.life*0.3); fxCtx.beginPath(); fxCtx.arc(0,0,s*1.5,0,Math.PI*2); fxCtx.fill(); fxCtx.restore(); } if(fxParticles.length>0) requestAnimationFrame(fxLoop); else fxRunning = false; } /* ===== THEME TOGGLE ===== */ function setTheme(theme){ document.documentElement.setAttribute('data-theme', theme); localStorage.setItem('slot-theme', theme); const icon = $('themeIcon'); if(theme==='dark'){ icon.innerHTML = '<path d="M21 12.79A9 9 0 1111.21 3a7 7 0 009.79 9.79z"/>'; } else { icon.innerHTML = '<circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>'; } } $('themeBtn').addEventListener('click', ()=>{ const cur = document.documentElement.getAttribute('data-theme'); setTheme(cur==='dark'?'light':'dark'); }); /* ===== PAYTABLE ===== */ function buildPaytable(){ const inner = $('ptInner'); inner.innerHTML = ''; /* Three of a kind */ const order = ['seven','bar','diamond','bell','cherry','lemon']; for(const s of order){ const row = document.createElement('div'); row.className = 'pt-row'; const syms = document.createElement('div'); syms.className = 'pt-syms'; syms.innerHTML = symSVG(s)+symSVG(s)+symSVG(s); const mult = document.createElement('span'); mult.className = 'pt-mult' + (s==='seven'?' jp':''); mult.textContent = PAYOUTS_3[s]+'x'; row.appendChild(syms); row.appendChild(mult); inner.appendChild(row); } /* Two 7s */ const r2 = document.createElement('div'); r2.className = 'pt-row'; r2.querySelector; // noop const s2 = document.createElement('div'); s2.className = 'pt-syms'; s2.innerHTML = symSVG('seven')+symSVG('seven')+'<span class="pt-wild">ANY</span>'; const m2 = document.createElement('span'); m2.className = 'pt-mult'; m2.textContent = PAYOUT_2_SEVENS+'x'; r2.appendChild(s2); r2.appendChild(m2); inner.appendChild(r2); /* One 7 */ const r1 = document.createElement('div'); r1.className = 'pt-row'; const s1 = document.createElement('div'); s1.className = 'pt-syms'; s1.innerHTML = symSVG('seven')+'<span class="pt-wild">ANY ANY</span>'; const m1 = document.createElement('span'); m1.className = 'pt-mult'; m1.textContent = PAYOUT_1_SEVEN+'x'; r1.appendChild(s1); r1.appendChild(m1); inner.appendChild(r1); } $('ptToggle').addEventListener('click', ()=>{ const btn = $('ptToggle'); const content = $('ptContent'); btn.classList.toggle('open'); content.classList.toggle('open'); }); /* ===== EVENT LISTENERS ===== */ spinBtn.addEventListener('click', spin); $('betDn').addEventListener('click', ()=>{ if(spinning) return; if(betIdx > 0){ betIdx--; updateBet(); } }); $('betUp').addEventListener('click', ()=>{ if(spinning) return; if(betIdx < BETS.length-1){ betIdx++; updateBet(); } }); $('resetBtn').addEventListener('click', ()=>{ credits = 1000; betIdx = 1; goOverlay.classList.remove('vis'); clearWin(); updateCredits(); updateBet(); visible = [[rSym(),rSym(),rSym()],[rSym(),rSym(),rSym()],[rSym(),rSym(),rSym()]]; renderInitialReels(); }); /* Keyboard: Space or Enter to spin */ document.addEventListener('keydown', e=>{ if((e.code==='Space'||e.code==='Enter') && document.activeElement===document.body){ e.preventDefault(); spin(); } }); /* ===== INIT ===== */ (function init(){ /* Restore theme */ const saved = localStorage.getItem('slot-theme'); if(saved) setTheme(saved); else setTheme('dark'); renderInitialReels(); updateCredits(); updateBet(); buildPaytable(); })(); </script> </body> </html>
Cancel