tower-text-twist / index.html
LukasBe's picture
Add 2 files
647c264 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sparkle Tower</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&display=swap');
body {
font-family: 'Poppins', sans-serif;
background: linear-gradient(135deg, #f5f7fa 0%, #ffeef8 100%);
}
.letter-tile {
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
background: linear-gradient(135deg, #ffffff 0%, #f9f0ff 100%);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08);
border-radius: 12px;
position: relative;
overflow: hidden;
}
.letter-tile::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(
to bottom right,
rgba(255, 255, 255, 0.3) 0%,
rgba(255, 255, 255, 0) 60%
);
transform: rotate(30deg);
transition: all 0.3s ease;
}
.letter-tile:hover::before {
left: 100%;
}
.letter-tile.selected {
transform: translateY(-5px) scale(1.05);
box-shadow: 0 10px 25px rgba(168, 85, 247, 0.3);
background: linear-gradient(135deg, #f3e8ff 0%, #e9d5ff 100%);
color: #7e22ce;
}
.feedback-message {
animation: fadeInOut 2.5s ease-out forwards;
}
@keyframes fadeInOut {
0% { opacity: 0; transform: translateY(10px); }
20% { opacity: 1; transform: translateY(0); }
80% { opacity: 1; transform: translateY(0); }
100% { opacity: 0; transform: translateY(-10px); }
}
.timer-pulse {
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.1); }
}
#tower-container {
position: relative;
height: 250px;
overflow: hidden;
background: linear-gradient(to bottom, #faf5ff, #f3e8ff);
border-radius: 16px;
margin: 16px;
box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.05);
}
#tower-canvas {
position: absolute;
bottom: 0;
left: 50%;
transform-origin: center bottom;
transform: translateX(-50%);
transition: transform 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
@keyframes towerSway {
0%, 100% { transform: translateX(-50%) rotate(0.5deg); }
50% { transform: translateX(-50%) rotate(-0.5deg); }
}
.word-badge {
position: relative;
overflow: hidden;
transition: all 0.3s ease;
}
.word-badge::after {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(
to bottom right,
rgba(255, 255, 255, 0.4) 0%,
rgba(255, 255, 255, 0) 60%
);
transform: rotate(30deg);
transition: all 0.5s ease;
}
.word-badge:hover::after {
left: 100%;
}
.sparkle {
position: absolute;
width: 4px;
height: 4px;
background: white;
border-radius: 50%;
pointer-events: none;
opacity: 0;
animation: sparkle 1s ease-out forwards;
}
@keyframes sparkle {
0% { transform: scale(0); opacity: 0; }
50% { transform: scale(1); opacity: 1; }
100% { transform: scale(0); opacity: 0; }
}
.floating {
animation: float 3s ease-in-out infinite;
}
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
.btn-primary {
background: linear-gradient(135deg, #a855f7 0%, #8b5cf6 100%);
box-shadow: 0 4px 15px rgba(168, 85, 247, 0.3);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(168, 85, 247, 0.4);
}
.btn-primary::after {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(
to bottom right,
rgba(255, 255, 255, 0.3) 0%,
rgba(255, 255, 255, 0) 60%
);
transform: rotate(30deg);
transition: all 0.5s ease;
}
.btn-primary:hover::after {
left: 100%;
}
.btn-secondary {
background: linear-gradient(135deg, #f5f3ff 0%, #ede9fe 100%);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.btn-secondary:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.12);
}
.btn-secondary::after {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(
to bottom right,
rgba(255, 255, 255, 0.4) 0%,
rgba(255, 255, 255, 0) 60%
);
transform: rotate(30deg);
transition: all 0.5s ease;
}
.btn-secondary:hover::after {
left: 100%;
}
.crown {
position: absolute;
top: -15px;
left: 50%;
transform: translateX(-50%);
font-size: 24px;
color: #f59e0b;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
z-index: 10;
}
</style>
</head>
<body class="min-h-screen flex items-center justify-center p-4">
<div class="max-w-md w-full bg-white rounded-3xl shadow-2xl overflow-hidden">
<!-- Game Header -->
<div class="relative bg-gradient-to-r from-purple-500 to-pink-500 p-6 text-white">
<div class="absolute top-0 left-0 w-full h-full opacity-10">
<div class="absolute top-10 left-10 w-20 h-20 rounded-full bg-white"></div>
<div class="absolute top-20 right-20 w-32 h-32 rounded-full bg-white"></div>
<div class="absolute bottom-10 left-1/4 w-24 h-24 rounded-full bg-white"></div>
</div>
<h1 class="text-3xl font-bold text-center relative z-10 floating">Sparkle Tower</h1>
<p class="text-center text-purple-100 relative z-10">Build your dream tower with words!</p>
<div class="absolute -bottom-6 left-1/2 transform -translate-x-1/2 w-12 h-12 bg-white rounded-full flex items-center justify-center shadow-lg">
<div class="w-8 h-8 bg-gradient-to-r from-purple-500 to-pink-500 rounded-full flex items-center justify-center text-white">
<i class="fas fa-star"></i>
</div>
</div>
</div>
<!-- Tower Visualization -->
<div id="tower-container" class="relative mt-8">
<div class="absolute inset-0 flex items-center justify-center" id="tower-placeholder">
<div class="text-center text-purple-300">
<i class="fas fa-cloud text-5xl mb-2"></i>
<p class="text-lg">Your tower will appear here!</p>
</div>
</div>
<canvas id="tower-canvas" width="300" height="800"></canvas>
</div>
<!-- Game Info -->
<div class="p-6 bg-gradient-to-r from-purple-50 to-pink-50 flex justify-between items-center relative">
<div class="text-center">
<p class="text-xs text-purple-500 font-semibold">SCORE</p>
<p id="score" class="text-3xl font-bold text-purple-800">0</p>
</div>
<div class="text-center">
<p class="text-xs text-purple-500 font-semibold">HEIGHT</p>
<p id="height" class="text-3xl font-bold text-purple-800">0</p>
</div>
<div class="text-center relative">
<p class="text-xs text-purple-500 font-semibold">TIME</p>
<p id="timer" class="text-3xl font-bold text-pink-600">60</p>
<div class="absolute -top-2 -right-2 w-6 h-6 bg-pink-500 rounded-full flex items-center justify-center text-white text-xs">
<i class="fas fa-clock"></i>
</div>
</div>
</div>
<!-- Feedback Message -->
<div id="feedback" class="h-16 flex items-center justify-center bg-gradient-to-r from-purple-50 to-pink-50">
<p id="feedback-message" class="text-lg font-semibold opacity-0"></p>
</div>
<!-- Letter Tiles -->
<div id="letter-area" class="p-6 grid grid-cols-6 gap-3">
<!-- Letter tiles will be generated here -->
</div>
<!-- Current Word -->
<div class="px-6 py-4 bg-gradient-to-r from-purple-50 to-pink-50">
<div class="bg-white rounded-xl p-4 shadow-inner relative">
<p class="text-xs text-purple-400 mb-1">Current Word:</p>
<p id="current-word" class="text-3xl font-mono text-center min-h-10 text-purple-800">-</p>
<div class="absolute top-0 right-0 mt-2 mr-3 text-purple-300">
<i class="fas fa-pencil-alt"></i>
</div>
</div>
</div>
<!-- Controls -->
<div class="p-6 flex gap-4">
<button id="submit-btn" class="flex-1 btn-primary text-white font-bold py-4 px-6 rounded-xl transition disabled:opacity-50 disabled:cursor-not-allowed" disabled>
<i class="fas fa-check mr-2"></i> Submit
</button>
<button id="clear-btn" class="flex-1 btn-secondary text-purple-800 font-bold py-4 px-6 rounded-xl transition">
<i class="fas fa-eraser mr-2"></i> Clear
</button>
</div>
<!-- Found Words -->
<div class="p-6 bg-gradient-to-r from-purple-50 to-pink-50 border-t border-purple-100">
<div class="flex justify-between items-center mb-2">
<p class="text-xs text-purple-400">Found Words (<span id="found-count">0</span>):</p>
<div class="text-purple-300">
<i class="fas fa-trophy"></i>
</div>
</div>
<div id="found-words" class="flex flex-wrap gap-2">
<!-- Found words will appear here -->
</div>
</div>
<!-- Start Button -->
<div class="p-6 bg-white">
<button id="start-btn" class="w-full btn-primary text-white font-bold py-4 px-6 rounded-xl transition">
<i class="fas fa-play mr-2"></i> Start Game
</button>
</div>
</div>
<script>
// Game configuration
const config = {
roundTime: 60,
availableLetters: ['A', 'E', 'S', 'T', 'R', 'N', 'L', 'O', 'I', 'D'],
validWords: [
"ART", "EAT", "NET", "RAT", "RENT", "STAR", "START", "TAN", "TEA", "TEN",
"NEST", "RATE", "REST", "SAT", "SEA", "SENT", "SET", "TERN", "EARN", "EAST",
"EATS", "NEAT", "RANT", "SEAT", "STAR", "TEAR", "TENS", "ANTS", "ARTS", "ERAS",
"NATS", "NEAR", "NEST", "RATS", "SANE", "TARE", "TARN", "TARS", "TEAS", "TENS",
"LOVE", "DREAM", "STAR", "ROSE", "PEARL", "LOTUS", "DIARY", "STORY", "TALES", "SONG",
"DANCE", "MELODY", "HEART", "SWEET", "CANDY", "SUNNY", "LIGHT", "ANGEL", "FAIRY", "MAGIC"
],
blockColors: [
'#FF9FF3', '#FECA57', '#FF6B6B', '#48DBFB', '#1DD1A1',
'#F368E0', '#FF9FF3', '#00D2D3', '#54A0FF', '#5F27CD',
'#C56CF0', '#FFB8B8', '#FF9F43', '#EE5253', '#0ABDE3',
'#10AC84', '#2E86DE', '#341F97', '#B33771', '#6D214F'
]
};
// Game state
const state = {
gameActive: false,
timer: config.roundTime,
score: 0,
height: 0,
selectedLetters: [],
foundWords: [],
timerInterval: null,
towerBlocks: [],
swayAnimation: null
};
// DOM elements
const elements = {
letterArea: document.getElementById('letter-area'),
currentWord: document.getElementById('current-word'),
score: document.getElementById('score'),
height: document.getElementById('height'),
timer: document.getElementById('timer'),
feedback: document.getElementById('feedback-message'),
submitBtn: document.getElementById('submit-btn'),
clearBtn: document.getElementById('clear-btn'),
startBtn: document.getElementById('start-btn'),
foundWords: document.getElementById('found-words'),
foundCount: document.getElementById('found-count'),
towerCanvas: document.getElementById('tower-canvas'),
towerCtx: document.getElementById('tower-canvas').getContext('2d'),
towerContainer: document.getElementById('tower-container'),
towerPlaceholder: document.getElementById('tower-placeholder')
};
// Initialize the game
function initGame() {
// Create letter tiles
elements.letterArea.innerHTML = '';
config.availableLetters.forEach(letter => {
const tile = document.createElement('div');
tile.className = 'letter-tile flex items-center justify-center text-3xl font-bold cursor-pointer h-16';
tile.textContent = letter;
tile.dataset.letter = letter;
// Add sparkle effect on click
tile.addEventListener('click', (e) => {
toggleLetter(letter, tile);
createSparkle(e);
});
elements.letterArea.appendChild(tile);
});
// Reset game state
state.gameActive = false;
state.timer = config.roundTime;
state.score = 0;
state.height = 0;
state.selectedLetters = [];
state.foundWords = [];
state.towerBlocks = [];
// Clear found words
elements.foundWords.innerHTML = '';
// Stop any existing sway animation
if (state.swayAnimation) {
cancelAnimationFrame(state.swayAnimation);
}
// Reset tower canvas
elements.towerCanvas.style.transform = 'translateX(-50%) scale(1)';
elements.towerCanvas.style.animation = 'none';
elements.towerCtx.clearRect(0, 0, elements.towerCanvas.width, elements.towerCanvas.height);
// Show placeholder
elements.towerPlaceholder.style.display = 'flex';
// Update UI
updateUI();
}
// Create sparkle effect
function createSparkle(event) {
const sparkle = document.createElement('div');
sparkle.className = 'sparkle';
// Position the sparkle at the click location
const rect = event.currentTarget.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
sparkle.style.left = `${x}px`;
sparkle.style.top = `${y}px`;
event.currentTarget.appendChild(sparkle);
// Remove after animation
setTimeout(() => {
sparkle.remove();
}, 1000);
}
// Calculate the optimal scale for the tower
function calculateTowerScale() {
const containerHeight = elements.towerContainer.clientHeight;
const towerHeight = state.towerBlocks.length * 25 + 40; // 25px per block + some margin
// Calculate scale to fit the tower in the container
const scale = Math.min(1, containerHeight / towerHeight * 0.85);
return scale;
}
// Draw the tower with current scale
function drawTower() {
const scale = calculateTowerScale();
// Apply the scale transform
elements.towerCanvas.style.transform = `translateX(-50%) scale(${scale})`;
// Clear canvas
elements.towerCtx.clearRect(0, 0, elements.towerCanvas.width, elements.towerCanvas.height);
// Draw sky gradient
const skyGradient = elements.towerCtx.createLinearGradient(0, 0, 0, elements.towerCanvas.height);
skyGradient.addColorStop(0, '#f3e8ff');
skyGradient.addColorStop(1, '#fae8ff');
elements.towerCtx.fillStyle = skyGradient;
elements.towerCtx.fillRect(0, 0, elements.towerCanvas.width, elements.towerCanvas.height);
// Draw ground
elements.towerCtx.fillStyle = '#e9d5ff';
elements.towerCtx.fillRect(0, elements.towerCanvas.height - 15, elements.towerCanvas.width, 15);
// Draw grass details
elements.towerCtx.fillStyle = '#a855f7';
for (let i = 0; i < 20; i++) {
const x = Math.random() * elements.towerCanvas.width;
const height = 5 + Math.random() * 10;
elements.towerCtx.fillRect(x, elements.towerCanvas.height - 15, 2, -height);
}
// Draw each block in the tower
state.towerBlocks.forEach((block, index) => {
const yPos = elements.towerCanvas.height - 25 - (index * 25);
// Add slight horizontal offset for "wavy" effect
const waveOffset = Math.sin(Date.now() / 500 + index * 0.3) * 4;
// Block
elements.towerCtx.fillStyle = block.color;
elements.towerCtx.beginPath();
elements.towerCtx.roundRect(block.x + waveOffset, yPos, block.width, 20, [0, 0, 8, 8]);
elements.towerCtx.fill();
// Block pattern
elements.towerCtx.fillStyle = 'rgba(255, 255, 255, 0.3)';
for (let i = 0; i < block.width / 15; i++) {
const x = block.x + waveOffset + 5 + i * 15;
elements.towerCtx.beginPath();
elements.towerCtx.arc(x, yPos + 10, 2, 0, Math.PI * 2);
elements.towerCtx.fill();
}
// Block border
elements.towerCtx.strokeStyle = 'rgba(255, 255, 255, 0.5)';
elements.towerCtx.lineWidth = 1;
elements.towerCtx.beginPath();
elements.towerCtx.roundRect(block.x + waveOffset, yPos, block.width, 20, [0, 0, 8, 8]);
elements.towerCtx.stroke();
});
// Draw tower top if there are blocks
if (state.towerBlocks.length > 0) {
const topBlock = state.towerBlocks[state.towerBlocks.length - 1];
const topY = elements.towerCanvas.height - 25 - (state.towerBlocks.length * 25);
const waveOffset = Math.sin(Date.now() / 500 + state.towerBlocks.length * 0.3) * 4;
// Flag
elements.towerCtx.fillStyle = '#FF9FF3';
elements.troyCtx.beginPath();
elements.towerCtx.moveTo(topBlock.x + topBlock.width/2 + waveOffset, topY - 15);
elements.towerCtx.lineTo(topBlock.x + topBlock.width/2 + 20 + waveOffset, topY - 5);
elements.towerCtx.lineTo(topBlock.x + topBlock.width/2 + waveOffset, topY + 5);
elements.towerCtx.closePath();
elements.towerCtx.fill();
// Flag details
elements.towerCtx.fillStyle = '#FF6B6B';
elements.towerCtx.beginPath();
elements.towerCtx.arc(topBlock.x + topBlock.width/2 + 10 + waveOffset, topY - 10, 3, 0, Math.PI * 2);
elements.towerCtx.fill();
// Flag pole
elements.towerCtx.strokeStyle = '#FFFFFF';
elements.towerCtx.lineWidth = 2;
elements.towerCtx.beginPath();
elements.towerCtx.moveTo(topBlock.x + topBlock.width/2 + waveOffset, topY);
elements.towerCtx.lineTo(topBlock.x + topBlock.width/2 + waveOffset, topY - 15);
elements.towerCtx.stroke();
// Add crown to the tower if it's tall enough
if (state.towerBlocks.length > 10) {
elements.towerCtx.fillStyle = '#FECA57';
elements.towerCtx.beginPath();
elements.towerCtx.moveTo(topBlock.x + topBlock.width/2 + waveOffset - 15, topY - 25);
elements.towerCtx.lineTo(topBlock.x + topBlock.width/2 + waveOffset, topY - 40);
elements.towerCtx.lineTo(topBlock.x + topBlock.width/2 + waveOffset + 15, topY - 25);
elements.towerCtx.lineTo(topBlock.x + topBlock.width/2 + waveOffset + 10, topY - 25);
elements.towerCtx.lineTo(topBlock.x + topBlock.width/2 + waveOffset, topY - 35);
elements.towerCtx.lineTo(topBlock.x + topBlock.width/2 + waveOffset - 10, topY - 25);
elements.towerCtx.closePath();
elements.towerCtx.fill();
elements.towerCtx.strokeStyle = '#FF9F43';
elements.towerCtx.lineWidth = 1;
elements.towerCtx.stroke();
}
}
// Continue the animation loop for the waving effect
if (state.gameActive) {
state.swayAnimation = requestAnimationFrame(drawTower);
}
}
// Add a block to the tower
function addTowerBlock(wordLength) {
// Hide placeholder when first block is added
if (state.towerBlocks.length === 0) {
elements.towerPlaceholder.style.display = 'none';
}
// Determine block properties
const color = config.blockColors[Math.floor(Math.random() * config.blockColors.length)];
const width = 50 + (wordLength * 6); // Bigger blocks for longer words
const x = (elements.towerCanvas.width - width) / 2; // Center the block
// Add slight random offset for visual interest
const xOffset = Math.random() * 10 - 5;
// Add the block to our tower
state.towerBlocks.push({
x: x + xOffset,
width: width,
color: color,
wordLength: wordLength
});
// Start the waving animation if not already running
if (!state.swayAnimation) {
drawTower();
}
// Add a little growth animation
animateTowerGrowth();
// Add floating hearts occasionally
if (Math.random() > 0.7) {
createFloatingHeart();
}
}
// Create floating heart effect
function createFloatingHeart() {
const heart = document.createElement('div');
heart.className = 'absolute text-pink-400 text-xl';
heart.innerHTML = '<i class="fas fa-heart"></i>';
// Position at random location in tower container
const containerRect = elements.towerContainer.getBoundingClientRect();
const x = Math.random() * containerRect.width;
heart.style.left = `${x}px`;
heart.style.bottom = '0';
heart.style.opacity = '0';
heart.style.transform = 'translateY(0)';
elements.towerContainer.appendChild(heart);
// Animate heart floating up
setTimeout(() => {
heart.style.transition = 'all 3s ease-out';
heart.style.opacity = '1';
heart.style.transform = `translateY(-${containerRect.height}px)`;
// Remove after animation
setTimeout(() => {
heart.remove();
}, 3000);
}, 0);
}
// Animate tower growth
function animateTowerGrowth() {
let scale = calculateTowerScale();
let tempScale = scale * 1.1; // Start slightly larger
const animate = () => {
if (tempScale <= scale) return;
tempScale -= 0.005;
elements.towerCanvas.style.transform = `translateX(-50%) scale(${tempScale})`;
requestAnimationFrame(animate);
};
animate();
}
// Start a new round
function startRound() {
if (state.timerInterval) clearInterval(state.timerInterval);
initGame();
state.gameActive = true;
// Start timer
state.timerInterval = setInterval(() => {
state.timer--;
updateUI();
if (state.timer <= 0) {
endRound();
}
// Add timer urgency effect
if (state.timer <= 10 && !elements.timer.classList.contains('timer-pulse')) {
elements.timer.classList.add('timer-pulse');
}
}, 1000);
showFeedback("✨ Build your dream tower with words! ✨", 2000);
}
// End the current round
function endRound() {
state.gameActive = false;
clearInterval(state.timerInterval);
// Special message based on performance
let message;
if (state.score >= 500) {
message = `Amazing! You scored ${state.score} points! πŸ‘‘`;
} else if (state.score >= 300) {
message = `Great job! You scored ${state.score} points! πŸ’–`;
} else {
message = `You scored ${state.score} points! Try again! ✨`;
}
showFeedback(message, 3000);
// Stop the sway animation
if (state.swayAnimation) {
cancelAnimationFrame(state.swayAnimation);
state.swayAnimation = null;
}
}
// Toggle letter selection
function toggleLetter(letter, tile) {
if (!state.gameActive) return;
const index = state.selectedLetters.indexOf(letter);
if (index === -1) {
// Select the letter
state.selectedLetters.push(letter);
tile.classList.add('selected');
} else {
// Deselect the letter (remove last occurrence)
const lastIndex = state.selectedLetters.lastIndexOf(letter);
if (lastIndex !== -1) {
state.selectedLetters.splice(lastIndex, 1);
}
tile.classList.remove('selected');
}
updateUI();
}
// Clear selected letters
function clearSelection() {
if (!state.gameActive) return;
state.selectedLetters = [];
document.querySelectorAll('.letter-tile').forEach(tile => {
tile.classList.remove('selected');
});
updateUI();
}
// Submit current word
function submitWord() {
if (!state.gameActive || state.selectedLetters.length < 3) {
showFeedback(state.selectedLetters.length < 3 ? "Word too short! (min 3 letters)" : "Game not active", 1500);
return;
}
const word = state.selectedLetters.join('').toUpperCase();
if (state.foundWords.includes(word)) {
showFeedback("Already found! Try another word", 1500);
} else if (config.validWords.includes(word)) {
// Valid word
state.foundWords.push(word);
state.score += word.length * 10;
state.height += word.length;
// Add a block to the tower for this word
addTowerBlock(word.length);
// Add to found words display
const wordBadge = document.createElement('span');
wordBadge.className = 'word-badge bg-gradient-to-r from-purple-100 to-pink-100 text-purple-800 text-sm font-medium px-3 py-1 rounded-full';
wordBadge.textContent = word;
elements.foundWords.appendChild(wordBadge);
// Add sparkle effect to the badge
wordBadge.addEventListener('mouseenter', (e) => {
for (let i = 0; i < 3; i++) {
setTimeout(() => {
createSparkle(e);
}, i * 200);
}
});
showFeedback(`+${word.length * 10} points! ${getRandomEmoji()}`, 1500);
// Clear the selection after successful submission
clearSelection();
} else {
showFeedback("Not in our dictionary", 1500);
}
updateUI();
}
// Get random celebratory emoji
function getRandomEmoji() {
const emojis = ['✨', '🌸', 'πŸ’–', 'πŸŽ€', '🌈', 'πŸ’«', '🌟', 'πŸ’Ž'];
return emojis[Math.floor(Math.random() * emojis.length)];
}
// Show feedback message
function showFeedback(message, duration) {
elements.feedback.textContent = message;
elements.feedback.className = 'text-lg font-semibold feedback-message';
// Set color based on message type
if (message.includes("Not") || message.includes("Already") || message.includes("short")) {
elements.feedback.classList.add('text-pink-500');
} else if (message.includes("points")) {
elements.feedback.classList.add('text-purple-600');
} else {
elements.feedback.classList.add('text-pink-400');
}
}
// Update UI elements
function updateUI() {
// Update current word display
elements.currentWord.textContent = state.selectedLetters.length > 0
? state.selectedLetters.join('')
: '-';
// Update score and height
elements.score.textContent = state.score;
elements.height.textContent = state.height;
elements.timer.textContent = state.timer;
elements.foundCount.textContent = state.foundWords.length;
// Update timer color when low
if (state.timer <= 10) {
elements.timer.classList.add('text-pink-600');
} else {
elements.timer.classList.remove('text-pink-600');
}
// Enable/disable submit button
elements.submitBtn.disabled = !state.gameActive || state.selectedLetters.length < 3;
}
// Event listeners
elements.startBtn.addEventListener('click', startRound);
elements.clearBtn.addEventListener('click', clearSelection);
elements.submitBtn.addEventListener('click', submitWord);
// Add sparkle effect to buttons on hover
[elements.startBtn, elements.submitBtn, elements.clearBtn].forEach(btn => {
btn.addEventListener('mouseenter', (e) => {
for (let i = 0; i < 3; i++) {
setTimeout(() => {
createSparkle(e);
}, i * 200);
}
});
});
// Initialize the game on load
initGame();
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=LukasBe/tower-text-twist" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>