.
Neon Cyber Arena · Canvas Sway Animation · Web Audio API Sounds · Confetti Win · Neon Keyboard · CSS Scanline · 3 Word Categories · No Build Tools
The Coodeverse Hangman Cyber Arena 2025 is a free neon cyberpunk word guessing game built with pure HTML CSS JavaScript. Features HTML5 Canvas hangman drawing with 6-part progressive reveal, Math.sin sway animation on game over, Web Audio API oscillator synth sounds for correct/wrong/win events, canvas-confetti side-cannon win celebration, win/streak tracking with JavaScript Set guess deduplication, 3 word categories (JS terms, fun, cyberpunk), CSS scanline overlay animation, animated gradient text with drop-shadow glow, glassmorphism game card, and keyboard + click dual input. Pure vanilla JavaScript except canvas-confetti. 100% free.
Pure HTML5 Canvas + Vanilla JavaScript. Teaches canvas hangman drawing with sway physics, Web Audio API oscillator sound design, canvas-confetti celebration, JavaScript Set data structure, CSS scanline overlay, animated gradient text, glassmorphism, and dynamic keyboard generation. One of the most comprehensive free word game tutorials.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> <title>Hangman: Cyber Arena | Coodeverse</title> <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@600;800&family=Rajdhani:wght@500;700" rel="stylesheet"> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/confetti.browser.min.js"></script> <style> :root { --bg:#0a001f; --primary:#00ffff; --accent:#ff00ff; --glass:rgba(20,20,60,0.5); --text:#e0f7ff; --danger:#ff0066; } * { margin:0; padding:0; box-sizing:border-box; } body { background: var(--bg); background-image: radial-gradient(circle at 20% 80%,rgba(120,0,255,0.3) 0%,transparent 50%), radial-gradient(circle at 80% 20%,rgba(0,255,255,0.3) 0%,transparent 50%), linear-gradient(135deg,#0f0033 0%,#000011 100%); color: var(--text); font-family:'Rajdhani',sans-serif; min-height:100vh; overflow-x:hidden; } /* CRT scanline overlay — pointer-events none so it doesn't block clicks */ .scanline { position:fixed; top:0; left:0; width:100%; height:100%; background:linear-gradient(transparent 50%,rgba(0,255,255,0.03) 50%); background-size:100% 4px; pointer-events:none; animation:scan 8s linear infinite; z-index:9999; } @keyframes scan { 0%{transform:translateY(-100%);} 100%{transform:translateY(100%);} } header { text-align:center; padding:2rem 1rem; } /* Animated gradient text — background-clip only, use filter:drop-shadow not text-shadow */ .title { font-family:'Orbitron',monospace; font-size:clamp(3rem,8vw,6rem); font-weight:800; background:linear-gradient(90deg,#00ffff,#ff00ff,#00ffff); background-size:200%; -webkit-background-clip:text; -webkit-text-fill-color:transparent; animation:gradient 6s ease infinite, glow 2s ease-in-out infinite alternate; } @keyframes gradient { 0%,100%{background-position:0%;} 50%{background-position:100%;} } @keyframes glow { from{filter:drop-shadow(0 0 20px #00ffff);} to{filter:drop-shadow(0 0 40px #ff00ff);} } .stats { display:flex; justify-content:center; gap:2rem; margin:1rem 0; font-size:1.3rem; font-weight:700; } .stat { background:rgba(0,255,255,0.1); padding:0.8rem 1.5rem; border-radius:50px; border:1px solid rgba(0,255,255,0.3); min-width:120px; text-align:center; } /* Glassmorphism game card */ #game-container { background:var(--glass); backdrop-filter:blur(20px); border:1px solid rgba(0,255,255,0.2); border-radius:30px; padding:3rem 2rem; max-width:800px; margin:2rem auto; box-shadow:0 0 50px rgba(0,255,255,0.3),inset 0 0 50px rgba(0,0,0,0.5); } #hangman-canvas { width:100%; height:300px; margin:0 auto 2rem; display:block; filter:drop-shadow(0 0 30px var(--primary)); } #word-display { display:flex; justify-content:center; gap:12px; flex-wrap:wrap; margin:2rem 0; } .letter { width:60px; height:80px; background:linear-gradient(135deg,rgba(0,255,255,0.2),rgba(255,0,255,0.1)); border:3px solid var(--primary); border-radius:16px; display:flex; align-items:center; justify-content:center; font-size:2.5rem; font-weight:bold; overflow:hidden; transition:all 0.4s; } .letter.revealed { background:linear-gradient(135deg,#00ffff,#ff00ff); color:#000; transform:scale(1.15) rotate(5deg); animation:pop 0.6s ease; } @keyframes pop { 0%{transform:scale(0) rotate(180deg);} 70%{transform:scale(1.3) rotate(-10deg);} 100%{transform:scale(1.15) rotate(5deg);} } #hint-text { text-align:center; font-size:1.4rem; margin:1.5rem 0; padding:1rem; background:rgba(255,0,255,0.1); border-radius:20px; border:1px dashed var(--accent); } #keyboard { display:grid; grid-template-columns:repeat(auto-fit,minmax(55px,1fr)); gap:12px; margin:2.5rem 0; } .key { background:linear-gradient(135deg,rgba(0,255,255,0.2),rgba(0,255,255,0.05)); border:2px solid var(--primary); color:white; font-family:'Orbitron',monospace; font-size:1.4rem; font-weight:bold; padding:18px 8px; border-radius:18px; cursor:pointer; transition:all 0.2s; text-transform:uppercase; } .key:hover { transform:translateY(-8px) scale(1.1); box-shadow:0 20px 40px rgba(0,255,255,0.4); background:var(--primary); color:black; } .key.correct { background:#00ff88!important; color:black; animation:correct 0.6s ease; } .key.wrong { background:var(--danger)!important; animation:wrong 0.6s ease; } @keyframes correct { 0%,100%{transform:scale(1);} 50%{transform:scale(1.3);} } @keyframes wrong { 0%,100%{transform:translateX(0);} 25%{transform:translateX(-10px);} 75%{transform:translateX(10px);} } #controls { display:flex; justify-content:center; gap:1rem; margin-top:2rem; flex-wrap:wrap; } #controls button { background:linear-gradient(135deg,var(--primary),var(--accent)); border:none; padding:1rem 2rem; border-radius:50px; color:black; font-weight:bold; font-size:1.2rem; cursor:pointer; transition:all 0.3s; } #controls button:hover { transform:translateY(-5px) scale(1.05); box-shadow:0 20px 40px rgba(0,255,255,0.6); } #message { text-align:center; font-size:2.5rem; font-weight:bold; min-height:80px; margin:2rem 0; text-shadow:0 0 20px var(--primary); } @media (max-width:600px) { .letter{width:50px;height:65px;font-size:2rem;} .key{padding:14px 4px;font-size:1.2rem;} } </style> </head> <body> <div class="scanline" aria-hidden="true"></div> <header> <div class="title" role="heading" aria-level="1">HANGMAN</div> <div class="stats" aria-label="Game statistics"> <div class="stat">Wins: <span id="wins">0</span></div> <div class="stat">Streak: <span id="streak">0</span></div> <div class="stat">Lives: <span id="lives">6</span></div> </div> </header> <div id="game-container"> <canvas id="hangman-canvas" width="400" height="300" aria-label="Hangman drawing"></canvas> <div id="word-display" role="group" aria-label="Word letters"></div> <div id="hint-text" aria-live="polite">Choose a category to begin...</div> <div id="keyboard" role="group" aria-label="Letter keyboard"></div> <div id="message" aria-live="assertive"></div> <div id="controls"> <button onclick="newGame('all')">New Game</button> <button onclick="newGame('js')">JS Only</button> <button onclick="newGame('fun')">Fun Words</button> </div> </div> <footer style="text-align:center;padding:2rem;color:#666;font-size:0.9rem;">CYBER HANGMAN 2025 • Built with pure JS & love — <a href="https://coodeverse.com" style="color:#00ffff;">Coodeverse</a></footer> <script> /* Hangman Cyber Arena | Coodeverse | CC BY 4.0 */ // ── Web Audio API Sound Effects ── // Creates a new AudioContext per sound (disposable pattern — avoids stale context) function playSound(type) { try { const ac = new (window.AudioContext || window.webkitAudioContext)(); const osc = ac.createOscillator(); const gain = ac.createGain(); osc.connect(gain); gain.connect(ac.destination); if (type === 'correct') { osc.frequency.setValueAtTime(800, ac.currentTime); osc.frequency.exponentialRampToValueAtTime(1200, ac.currentTime + 0.1); // rising chirp } else if (type === 'wrong') { osc.frequency.setValueAtTime(400, ac.currentTime); osc.frequency.exponentialRampToValueAtTime(100, ac.currentTime + 0.3); // falling buzz } else if (type === 'win') { osc.type = 'sine'; osc.frequency.setValueAtTime(600, ac.currentTime); osc.frequency.exponentialRampToValueAtTime(1200, ac.currentTime + 0.5); // triumphant sweep } gain.gain.setValueAtTime(0.3, ac.currentTime); gain.gain.exponentialRampToValueAtTime(0.01, ac.currentTime + 0.5); osc.start(); osc.stop(ac.currentTime + 0.5); } catch(e) { /* Audio not available — silently skip */ } } // ── Word Categories ── const categories = { all: [ {word:"JAVASCRIPT", hint:"The language powering the web"}, {word:"HANGMAN", hint:"This legendary word game"}, {word:"CYBERPUNK", hint:"Neon lights, high tech, low life"}, {word:"MATRIX", hint:"Red pill or blue pill?"}, {word:"GLITCH", hint:"Digital distortion"}, {word:"QUANTUM", hint:"Superposition of states"}, {word:"NEURALINK", hint:"Elon's brain interface"}, {word:"BLOCKCHAIN", hint:"Decentralized ledger"}, {word:"FIREWALL", hint:"Digital defense system"}, {word:"OVERCLOCK", hint:"Push hardware beyond limits"} ], js: [ {word:"PROMISE", hint:"Handles async operations"}, {word:"CLOSURE", hint:"Function + lexical environment"}, {word:"PROTOTYPE", hint:"Object inheritance mechanism"}, {word:"HOISTING", hint:"Variables lifted to top"}, {word:"EVENTLOOP", hint:"JS concurrency model"} ], fun: [ {word:"PIZZA", hint:"Italian masterpiece"}, {word:"TACO", hint:"Mexican folded joy"}, {word:"NINJA", hint:"Stealth master"}, {word:"UNICORN", hint:"Magical horse with horn"}, {word:"MEME", hint:"Internet culture unit"} ] }; let currentWord = "", wins = 0, streak = 0, wrong = 0; let guessed = new Set(); // O(1) lookup — faster than Array.includes for repeated checks const canvas = document.getElementById('hangman-canvas'); const ctx = canvas.getContext('2d'); let sway = 0; // incrementing angle for Math.sin sway animation // ── Canvas Hangman Drawing ── function drawHangman() { ctx.clearRect(0, 0, 400, 300); ctx.strokeStyle = '#00ffff'; ctx.lineWidth = 6; ctx.lineCap = 'round'; // Gallows — always drawn ctx.beginPath(); ctx.moveTo(50,280); ctx.lineTo(250,280); ctx.stroke(); // base ctx.beginPath(); ctx.moveTo(100,280); ctx.lineTo(100,50); ctx.lineTo(220,50); ctx.lineTo(220,80); ctx.stroke(); // pole + arm + rope // Sway: only active when wrong === 6 (game over) sway += 0.05; const sw = wrong === 6 ? Math.sin(sway * 3) * 20 : 0; // Math.sin(-1 to 1) * 20 = ±20px if (wrong >= 1) { ctx.beginPath(); ctx.arc(220+sw, 100, 25, 0, Math.PI*2); ctx.stroke(); // head if (wrong >= 6) { // X eyes on death ctx.beginPath(); ctx.moveTo(210+sw,90); ctx.lineTo(230+sw,110); ctx.moveTo(230+sw,90); ctx.lineTo(210+sw,110); ctx.stroke(); } } if (wrong >= 2) { ctx.beginPath(); ctx.moveTo(220+sw,125); ctx.lineTo(220+sw,200); ctx.stroke(); } // body if (wrong >= 3) { ctx.beginPath(); ctx.moveTo(220+sw,150); ctx.lineTo(180+sw,180); ctx.stroke(); } // left arm if (wrong >= 4) { ctx.beginPath(); ctx.moveTo(220+sw,150); ctx.lineTo(260+sw,180); ctx.stroke(); } // right arm if (wrong >= 5) { ctx.beginPath(); ctx.moveTo(220+sw,200); ctx.lineTo(180+sw,250); ctx.stroke(); } // left leg if (wrong >= 6) { ctx.beginPath(); ctx.moveTo(220+sw,200); ctx.lineTo(260+sw,250); ctx.stroke(); // right leg requestAnimationFrame(drawHangman); // loop sway animation } } function newGame(category = 'all') { const words = categories[category] || categories.all; const sel = words[Math.floor(Math.random() * words.length)]; currentWord = sel.word; guessed = new Set(); // fresh Set each game wrong = 0; document.getElementById('message').textContent = ''; document.getElementById('hint-text').textContent = 'Hint: ' + sel.hint; document.getElementById('lives').textContent = 6; buildKeyboard(); renderWord(); drawHangman(); } function renderWord() { const display = document.getElementById('word-display'); display.innerHTML = ''; [...currentWord].forEach(letter => { // spread string → array of chars const div = document.createElement('div'); div.className = 'letter'; if (guessed.has(letter)) { div.textContent = letter; div.classList.add('revealed'); } display.appendChild(div); }); } function buildKeyboard() { const kb = document.getElementById('keyboard'); kb.innerHTML = ''; // Generate A–Z using charCode 65–90 for (let i = 65; i <= 90; i++) { const letter = String.fromCharCode(i); const btn = document.createElement('div'); btn.className = 'key'; btn.textContent = letter; btn.setAttribute('role', 'button'); btn.setAttribute('aria-label', 'Letter ' + letter); btn.onclick = () => guess(letter, btn); kb.appendChild(btn); } } function guess(letter, btn) { if (guessed.has(letter) || wrong >= 6 || checkWin()) return; // prevent re-guess guessed.add(letter); if (currentWord.includes(letter)) { btn.classList.add('correct'); playSound('correct'); renderWord(); if (checkWin()) win(); } else { wrong++; btn.classList.add('wrong'); playSound('wrong'); document.getElementById('lives').textContent = 6 - wrong; drawHangman(); if (wrong >= 6) lose(); } } function checkWin() { return [...currentWord].every(l => guessed.has(l)); // spread + every + Set.has } function win() { wins++; streak++; document.getElementById('wins').textContent = wins; document.getElementById('streak').textContent = streak; document.getElementById('message').textContent = '🏆 GENIUS! YOU WIN!'; document.getElementById('message').style.color = '#00ff88'; playSound('win'); // canvas-confetti side-cannon: fires from both edges for 5 seconds const end = Date.now() + 5000; (function frame() { confetti({ particleCount: 7, angle: 60, spread: 55, origin: { x: 0 } }); // left cannon confetti({ particleCount: 7, angle: 120, spread: 55, origin: { x: 1 } }); // right cannon if (Date.now() < end) requestAnimationFrame(frame); }()); } function lose() { streak = 0; document.getElementById('streak').textContent = 0; document.getElementById('message').textContent = '💀 GAME OVER!'; document.getElementById('message').style.color = '#ff0066'; // Reveal word letter by letter with staggered setTimeout document.querySelectorAll('.letter').forEach((el, i) => { setTimeout(() => { el.textContent = currentWord[i]; el.classList.add('revealed'); }, i * 100); }); } // Physical keyboard support: map keydown to on-screen key by nth-child index window.addEventListener('keydown', e => { const upper = e.key.toUpperCase(); if (/^[A-Z]$/.test(upper)) { const key = document.querySelector(`.key:nth-child(${upper.charCodeAt(0) - 64})`); if (key) key.click(); } }); // Start game on DOM ready; also run sway animation loop every 50ms as fallback document.addEventListener('DOMContentLoaded', () => { newGame(); setInterval(drawHangman, 50); }); </script> </body> </html>
let guessed = new Set(); let wrong = 0. Build keyboard loop: for (let i = 65; i <= 90; i++) { String.fromCharCode(i) }. On guess: guessed.add(letter). Draw canvas parts progressively: head at wrong >= 1 through right leg at 6. Check win: [...word].every(l => guessed.has(l)). Check loss: wrong >= 6. Render word: show letter if guessed.has(letter), blank div otherwise. The Coodeverse Hangman Cyber Arena is a complete free cyberpunk example.sway += 0.05; const sw = wrong === 6 ? Math.sin(sway * 3) * 20 : 0. Apply to all body part x coordinates: ctx.arc(220 + sw, 100, 25, 0, Math.PI*2). Draw parts progressively: head at wrong >= 1, body >= 2, arms >= 3 & 4, legs >= 5 & 6. Add X eyes on death. On game over: if (wrong === 6) requestAnimationFrame(drawHangman) — this creates a self-calling loop that continues the sway until a new game starts.const ac = new AudioContext(). Create oscillator + gain: const osc = ac.createOscillator(); const gain = ac.createGain(). Connect: osc.connect(gain); gain.connect(ac.destination). Set frequency curve: osc.frequency.setValueAtTime(800, ac.currentTime); osc.frequency.exponentialRampToValueAtTime(1200, ac.currentTime + 0.1). Volume envelope: gain.gain.setValueAtTime(0.3, ac.currentTime); gain.gain.exponentialRampToValueAtTime(0.01, ac.currentTime + 0.5). Play: osc.start(); osc.stop(ac.currentTime + 0.5). No libraries needed.position:fixed; top:0; left:0; width:100%; height:100%; pointer-events:none; z-index:9999. Set repeating stripe background: background: linear-gradient(transparent 50%, rgba(0,255,255,0.03) 50%); background-size: 100% 4px. Animate scrolling: @keyframes scan { 0%{transform:translateY(-100%);} 100%{transform:translateY(100%);} } animation: scan 8s linear infinite. The 4px background-size creates 2px transparent + 2px tinted stripes. The translateY from -100% to +100% makes them scroll continuously downward like a real CRT scan.let guessed = new Set(). Check duplicate: if (guessed.has(letter)) return. Add: guessed.add(letter). Win check: [...currentWord].every(l => guessed.has(l)). Reset: guessed = new Set(). Set.has() is O(1) vs Array.includes() O(n). The spread operator [...currentWord] converts the string to a character array so .every() can iterate each letter.Canvas Sway · Web Audio Sounds · Confetti Win · Neon Keyboard · CSS Scanline · 3 Categories · No Build Tools · Free Forever
Trusted by 50,000+ developers worldwide · Coodeverse · Free Forever