Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>PixelCanvas - Multiplayer Drawing Game</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.7.2/socket.io.min.js"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css"> | |
<script> | |
tailwind.config = { | |
theme: { | |
extend: { | |
colors: { | |
primary: '#6366f1', | |
secondary: '#8b5cf6', | |
accent: '#ec4899', | |
dark: '#1e293b', | |
light: '#f8fafc' | |
} | |
} | |
} | |
} | |
</script> | |
<style> | |
.pixelated { | |
image-rendering: pixelated; | |
} | |
.glow { | |
box-shadow: 0 0 15px rgba(139, 92, 246, 0.5); | |
} | |
.canvas-container { | |
position: relative; | |
overflow: hidden; | |
border-radius: 0.75rem; | |
} | |
.drawing-cursor { | |
cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236366f1' stroke-width='2'%3E%3Ccircle cx='12' cy='12' r='5'/%3E%3C/svg%3E") 8 8, auto; | |
} | |
#messageContainer::-webkit-scrollbar { | |
width: 8px; | |
} | |
#messageContainer::-webkit-scrollbar-track { | |
background: #1e293b; | |
} | |
#messageContainer::-webkit-scrollbar-thumb { | |
background: #6366f1; | |
border-radius: 4px; | |
} | |
</style> | |
</head> | |
<body class="bg-dark text-light min-h-screen flex flex-col"> | |
<header class="bg-gradient-to-r from-primary to-secondary py-4 px-6"> | |
<div class="container mx-auto flex justify-between items-center"> | |
<div class="flex items-center space-x-2"> | |
<i class="fas fa-paint-brush text-3xl"></i> | |
<h1 class="text-2xl font-bold">PixelCanvas</h1> | |
</div> | |
<div id="playerStatus" class="flex items-center space-x-2"> | |
<div class="h-3 w-3 rounded-full bg-green-500 animate-pulse"></div> | |
<span id="onlineCount">0</span> players online | |
</div> | |
</div> | |
</header> | |
<main class="flex-grow container mx-auto px-4 py-8"> | |
<div id="gameScreen" class="hidden grid grid-cols-1 lg:grid-cols-3 gap-6"> | |
<div class="lg:col-span-2"> | |
<div class="bg-gray-800 rounded-xl p-4 shadow-lg"> | |
<div class="flex justify-between items-center mb-4"> | |
<h2 class="text-xl font-bold">Collaborative Canvas</h2> | |
<div id="timer" class="bg-secondary px-3 py-1 rounded-md">02:30</div> | |
</div> | |
<div class="canvas-container relative border-2 border-gray-700 rounded-lg bg-white glow"> | |
<canvas id="drawingCanvas" width="800" height="500" class="drawing-cursor w-full"></canvas> | |
<div class="absolute top-2 left-2 bg-gray-900 bg-opacity-80 text-white rounded-lg px-3 py-2 text-sm"> | |
<span id="currentTool">Brush</span> | |
</div> | |
</div> | |
<div class="mt-4 flex flex-wrap gap-2"> | |
<button id="brushBtn" class="tool-btn bg-primary px-3 py-2 rounded-md hover:opacity-90"> | |
<i class="fas fa-paintbrush mr-1"></i> Brush | |
</button> | |
<button id="eraserBtn" class="tool-btn bg-gray-600 px-3 py-2 rounded-md hover:opacity-90"> | |
<i class="fas fa-eraser mr-1"></i> Eraser | |
</button> | |
<button id="clearBtn" class="tool-btn bg-red-600 px-3 py-2 rounded-md hover:opacity-90"> | |
<i class="fas fa-trash mr-1"></i> Clear | |
</button> | |
<div class="ml-auto flex items-center"> | |
<span class="text-sm mr-2">Color:</span> | |
<input type="color" id="colorPicker" value="#6366f1" class="w-10 h-10 rounded cursor-pointer"> | |
</div> | |
<div class="flex items-center"> | |
<span class="text-sm mr-2">Size:</span> | |
<input type="range" id="brushSize" min="1" max="50" value="5" class="w-32 rounded"> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="lg:col-span-1 space-y-6"> | |
<div class="bg-gray-800 rounded-xl p-4 shadow-lg"> | |
<h2 class="text-xl font-bold mb-4">Players</h2> | |
<div id="playersList" class="space-y-2"> | |
<div class="flex items-center bg-gray-700 p-2 rounded-md"> | |
<div class="h-10 w-10 rounded-full bg-primary flex items-center justify-center mr-3"> | |
<span class="font-bold">P</span> | |
</div> | |
<div> | |
<span class="font-medium">Player1</span> | |
<div class="text-xs text-green-400">Drawing...</div> | |
</div> | |
<div class="ml-auto bg-secondary px-2 py-1 rounded text-sm">123</div> | |
</div> | |
</div> | |
</div> | |
<div class="bg-gray-800 rounded-xl p-4 shadow-lg"> | |
<h2 class="text-xl font-bold mb-4">Chat</h2> | |
<div id="messageContainer" class="h-64 overflow-y-auto mb-4 bg-gray-900 rounded-lg p-3"> | |
<div class="text-center text-gray-500 italic">Game starting...</div> | |
</div> | |
<div class="flex"> | |
<input type="text" id="messageInput" placeholder="Type your message..." class="flex-grow bg-gray-700 rounded-l-lg px-4 py-2 focus:outline-none"> | |
<button id="sendBtn" class="bg-primary hover:bg-indigo-600 px-4 py-2 rounded-r-lg"> | |
<i class="fas fa-paper-plane"></i> | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div id="lobby" class="max-w-2xl mx-auto"> | |
<div class="text-center py-12"> | |
<div class="bg-gradient-to-r from-primary to-secondary w-32 h-32 mx-auto rounded-full flex items-center justify-center mb-6 glow"> | |
<i class="fas fa-palette text-6xl text-white"></i> | |
</div> | |
<h1 class="text-4xl font-bold mb-2">PixelCanvas</h1> | |
<p class="text-gray-300 mb-8 text-lg">Collaborative drawing with players around the world</p> | |
<div class="bg-gray-800 rounded-xl p-8 mb-8"> | |
<div class="flex flex-col md:flex-row gap-4 mb-6"> | |
<input type="text" id="username" placeholder="Enter your username" class="bg-gray-700 rounded-lg px-4 py-3 focus:outline-none focus:ring-2 focus:ring-primary flex-grow" value="Player"> | |
<button id="joinBtn" class="bg-gradient-to-r from-primary to-secondary hover:from-indigo-700 hover:to-purple-700 rounded-lg px-8 py-3 font-medium transform transition hover:scale-105"> | |
<i class="fas fa-play mr-2"></i> Join Game | |
</button> | |
</div> | |
<div class="grid grid-cols-1 md:grid-cols-3 gap-4"> | |
<div class="bg-gray-900 p-4 rounded-lg"> | |
<div class="text-accent mb-2"><i class="fas fa-users fa-2x"></i></div> | |
<h3 class="font-bold mb-1">Multiplayer</h3> | |
<p class="text-sm text-gray-400">Play with anyone around the world</p> | |
</div> | |
<div class="bg-gray-900 p-4 rounded-lg"> | |
<div class="text-green-400 mb-2"><i class="fas fa-paint-brush fa-2x"></i></div> | |
<h3 class="font-bold mb-1">Real-time</h3> | |
<p class="text-sm text-gray-400">See drawings appear instantly</p> | |
</div> | |
<div class="bg-gray-900 p-4 rounded-lg"> | |
<div class="text-yellow-400 mb-2"><i class="fas fa-trophy fa-2x"></i></div> | |
<h3 class="font-bold mb-1">Competitive</h3> | |
<p class="text-sm text-gray-400">Earn points for creativity</p> | |
</div> | |
</div> | |
</div> | |
<div class="bg-gray-800 rounded-xl p-6"> | |
<h2 class="text-xl font-bold mb-4">How to Play</h2> | |
<ol class="space-y-3 text-left max-w-md mx-auto"> | |
<li class="flex items-start"> | |
<div class="bg-primary rounded-full h-6 w-6 flex items-center justify-center text-xs mr-3 mt-1">1</div> | |
<p>Join a game and wait for players</p> | |
</li> | |
<li class="flex items-start"> | |
<div class="bg-primary rounded-full h-6 w-6 flex items-center justify-center text-xs mr-3 mt-1">2</div> | |
<p>When your turn comes, draw the assigned word</p> | |
</li> | |
<li class="flex items-start"> | |
<div class="bg-primary rounded-full h-6 w-6 flex items-center justify-center text-xs mr-3 mt-1">3</div> | |
<p>Other players guess what you're drawing in chat</p> | |
</li> | |
<li class="flex items-start"> | |
<div class="bg-primary rounded-full h-6 w-6 flex items-center justify-center text-xs mr-3 mt-1">4</div> | |
<p>Earn points for correct guesses and creative drawings</p> | |
</li> | |
</ol> | |
</div> | |
</div> | |
</div> | |
</main> | |
<footer class="bg-gray-900 py-6"> | |
<div class="container mx-auto text-center text-gray-500"> | |
<p>PixelCanvas Multiplayer Game | Made with Socket.io, HTML, CSS & JavaScript</p> | |
<div class="mt-2 flex justify-center space-x-4"> | |
<a href="#" class="hover:text-primary"><i class="fab fa-github"></i></a> | |
<a href="#" class="hover:text-primary"><i class="fab fa-twitter"></i></a> | |
<a href="#" class="hover:text-primary"><i class="fab fa-discord"></i></a> | |
</div> | |
</div> | |
</footer> | |
<script> | |
document.addEventListener('DOMContentLoaded', () => { | |
// DOM elements | |
const lobby = document.getElementById('lobby'); | |
const gameScreen = document.getElementById('gameScreen'); | |
const joinBtn = document.getElementById('joinBtn'); | |
const usernameInput = document.getElementById('username'); | |
const onlineCount = document.getElementById('onlineCount'); | |
const playersList = document.getElementById('playersList'); | |
const messageContainer = document.getElementById('messageContainer'); | |
const messageInput = document.getElementById('messageInput'); | |
const sendBtn = document.getElementById('sendBtn'); | |
const drawingCanvas = document.getElementById('drawingCanvas'); | |
const brushBtn = document.getElementById('brushBtn'); | |
const eraserBtn = document.getElementById('eraserBtn'); | |
const clearBtn = document.getElementById('clearBtn'); | |
const colorPicker = document.getElementById('colorPicker'); | |
const brushSize = document.getElementById('brushSize'); | |
const currentTool = document.getElementById('currentTool'); | |
const timer = document.getElementById('timer'); | |
// Canvas context | |
const ctx = drawingCanvas.getContext('2d'); | |
// State variables | |
let isDrawing = false; | |
let lastX = 0; | |
let lastY = 0; | |
let currentColor = '#6366f1'; | |
let currentSize = 5; | |
let isEraser = false; | |
let socket; | |
// Initialize canvas | |
ctx.fillStyle = 'white'; | |
ctx.fillRect(0, 0, drawingCanvas.width, drawingCanvas.height); | |
ctx.lineJoin = 'round'; | |
ctx.lineCap = 'round'; | |
ctx.lineWidth = currentSize; | |
// Drawing functions | |
function startDrawing(e) { | |
isDrawing = true; | |
[lastX, lastY] = [e.offsetX, e.offsetY]; | |
} | |
function draw(e) { | |
if (!isDrawing) return; | |
ctx.strokeStyle = isEraser ? 'white' : currentColor; | |
ctx.lineWidth = currentSize; | |
ctx.beginPath(); | |
ctx.moveTo(lastX, lastY); | |
ctx.lineTo(e.offsetX, e.offsetY); | |
ctx.stroke(); | |
[lastX, lastY] = [e.offsetX, e.offsetY]; | |
// Simulate sending drawing data to server | |
const drawingData = { | |
startX: lastX, | |
startY: lastY, | |
endX: e.offsetX, | |
endY: e.offsetY, | |
color: isEraser ? 'white' : currentColor, | |
size: currentSize | |
}; | |
console.log("Drawing data:", drawingData); | |
} | |
function stopDrawing() { | |
isDrawing = false; | |
} | |
// Canvas event listeners | |
drawingCanvas.addEventListener('mousedown', startDrawing); | |
drawingCanvas.addEventListener('mousemove', draw); | |
drawingCanvas.addEventListener('mouseup', stopDrawing); | |
drawingCanvas.addEventListener('mouseout', stopDrawing); | |
// Tool selection | |
brushBtn.addEventListener('click', () => { | |
isEraser = false; | |
currentTool.textContent = 'Brush'; | |
brushBtn.classList.add('bg-primary'); | |
eraserBtn.classList.remove('bg-primary'); | |
eraserBtn.classList.add('bg-gray-600'); | |
}); | |
eraserBtn.addEventListener('click', () => { | |
isEraser = true; | |
currentTool.textContent = 'Eraser'; | |
eraserBtn.classList.add('bg-primary'); | |
eraserBtn.classList.remove('bg-gray-600'); | |
brushBtn.classList.remove('bg-primary'); | |
brushBtn.classList.add('bg-gray-600'); | |
}); | |
clearBtn.addEventListener('click', () => { | |
ctx.fillStyle = 'white'; | |
ctx.fillRect(0, 0, drawingCanvas.width, drawingCanvas.height); | |
}); | |
// Color and size pickers | |
colorPicker.addEventListener('input', (e) => { | |
currentColor = e.target.value; | |
}); | |
brushSize.addEventListener('input', (e) => { | |
currentSize = e.target.value; | |
}); | |
// Chat functionality | |
function sendMessage() { | |
const message = messageInput.value.trim(); | |
if (message) { | |
// Simulate message sending | |
addMessage("You", message, true); | |
// Clear input | |
messageInput.value = ''; | |
} | |
} | |
sendBtn.addEventListener('click', sendMessage); | |
messageInput.addEventListener('keypress', (e) => { | |
if (e.key === 'Enter') { | |
sendMessage(); | |
} | |
}); | |
function addMessage(name, text, isSelf = false) { | |
const messageElement = document.createElement('div'); | |
messageElement.classList.add('mb-3', 'last:mb-0'); | |
if (isSelf) { | |
messageElement.innerHTML = ` | |
<div class="flex justify-end"> | |
<div class="bg-primary text-white rounded-xl rounded-tr-none p-3 max-w-xs md:max-w-md"> | |
<div class="font-medium mb-1">${name}</div> | |
<div>${text}</div> | |
</div> | |
</div> | |
`; | |
} else { | |
messageElement.innerHTML = ` | |
<div class="flex"> | |
<div class="bg-gray-700 rounded-xl rounded-tl-none p-3 max-w-xs md:max-w-md"> | |
<div class="font-medium mb-1">${name}</div> | |
<div>${text}</div> | |
</div> | |
</div> | |
`; | |
} | |
messageContainer.appendChild(messageElement); | |
messageContainer.scrollTop = messageContainer.scrollHeight; | |
} | |
// Fake messages to simulate gameplay | |
function simulateGameplay() { | |
setTimeout(() => { | |
addMessage("System", "Word to draw: Cat"); | |
}, 1000); | |
setTimeout(() => { | |
addMessage("Player2", "Is it an animal?"); | |
}, 3000); | |
setTimeout(() => { | |
addMessage("Player3", "I think it's a cat!"); | |
addMessage("System", "Player3 guessed correctly! +10 points"); | |
}, 6000); | |
} | |
// Join game | |
joinBtn.addEventListener('click', () => { | |
const username = usernameInput.value || "Player"; | |
// Simulate connecting to the server | |
console.log(`Connecting as ${username}...`); | |
// Show loading state | |
joinBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Connecting...'; | |
joinBtn.disabled = true; | |
// Simulate connection process | |
setTimeout(() => { | |
// Switch to game screen | |
lobby.classList.add('hidden'); | |
gameScreen.classList.remove('hidden'); | |
// Add fake players | |
playersList.innerHTML = ` | |
<div class="flex items-center bg-gray-700 p-2 rounded-md"> | |
<div class="h-10 w-10 rounded-full bg-primary flex items-center justify-center mr-3"> | |
<span class="font-bold">${username.charAt(0)}</span> | |
</div> | |
<div> | |
<span class="font-medium">${username}</span> | |
<div class="text-xs text-purple-400">You</div> | |
</div> | |
<div class="ml-auto bg-secondary px-2 py-1 rounded text-sm">0</div> | |
</div> | |
<div class="flex items-center bg-gray-700 p-2 rounded-md"> | |
<div class="h-10 w-10 rounded-full bg-purple-500 flex items-center justify-center mr-3"> | |
<span class="font-bold">A</span> | |
</div> | |
<div> | |
<span class="font-medium">Artist123</span> | |
<div class="text-xs text-green-400">Drawing...</div> | |
</div> | |
<div class="ml-auto bg-secondary px-2 py-1 rounded text-sm">87</div> | |
</div> | |
<div class="flex items-center bg-gray-700 p-2 rounded-md"> | |
<div class="h-10 w-10 rounded-full bg-pink-500 flex items-center justify-center mr-3"> | |
<span class="font-bold">G</span> | |
</div> | |
<div> | |
<span class="font-medium">Guesser99</span> | |
<div class="text-xs text-gray-400">Waiting</div> | |
</div> | |
<div class="ml-auto bg-secondary px-2 py-1 rounded text-sm">42</div> | |
</div> | |
<div class="flex items-center bg-gray-700 p-2 rounded-md"> | |
<div class="h-10 w-10 rounded-full bg-yellow-500 flex items-center justify-center mr-3"> | |
<span class="font-bold">C</span> | |
</div> | |
<div> | |
<span class="font-medium">CreativeDrawer</span> | |
<div class="text-xs text-gray-400">Waiting</div> | |
</div> | |
<div class="ml-auto bg-secondary px-2 py-1 rounded text-sm">65</div> | |
</div> | |
`; | |
// Update online count | |
onlineCount.textContent = '4'; | |
// Start timer | |
startTimer(150); // 2 minutes 30 seconds | |
// Simulate gameplay messages | |
simulateGameplay(); | |
}, 1500); | |
}); | |
// Timer function | |
function startTimer(duration) { | |
let timerValue = duration; | |
const interval = setInterval(() => { | |
const minutes = Math.floor(timerValue / 60); | |
const seconds = timerValue % 60; | |
timer.textContent = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; | |
if (--timerValue < 0) { | |
clearInterval(interval); | |
timer.textContent = "00:00"; | |
addMessage("System", "Round completed! New round starting..."); | |
} | |
}, 1000); | |
} | |
// Initialize placeholder socket connection | |
console.log("Socket.IO client loaded. This would connect to a real server."); | |
}); | |
</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=LULDev/tttesttttttt" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |