nffnrnfn / index.html
gg9909's picture
Add 2 files
dbae5c2 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Neon 2048 - Futuristic Edition</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap');
:root {
--primary-color: #00f7ff;
--secondary-color: #ff00f7;
--bg-color: #0a0a20;
--grid-bg: rgba(15, 15, 40, 0.7);
--text-color: #e0e0ff;
--tile-bg: rgba(30, 30, 60, 0.5);
--glow: 0 0 10px var(--primary-color), 0 0 20px var(--primary-color);
--glow-secondary: 0 0 10px var(--secondary-color), 0 0 20px var(--secondary-color);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Orbitron', sans-serif;
background-color: var(--bg-color);
color: var(--text-color);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
overflow: hidden;
position: relative;
}
body::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
radial-gradient(circle at 20% 30%, var(--primary-color), transparent 30%),
radial-gradient(circle at 80% 70%, var(--secondary-color), transparent 30%);
opacity: 0.1;
z-index: -1;
}
.header {
text-align: center;
margin-bottom: 2rem;
position: relative;
}
h1 {
font-size: 3rem;
margin-bottom: 1rem;
text-transform: uppercase;
letter-spacing: 3px;
color: var(--primary-color);
text-shadow: var(--glow);
position: relative;
}
h1::after {
content: '';
position: absolute;
bottom: -10px;
left: 50%;
transform: translateX(-50%);
width: 100px;
height: 3px;
background: linear-gradient(90deg, transparent, var(--primary-color), transparent);
box-shadow: var(--glow);
}
.scores {
display: flex;
justify-content: center;
gap: 2rem;
margin-bottom: 1rem;
}
.score-box {
background: var(--tile-bg);
border: 1px solid var(--primary-color);
border-radius: 5px;
padding: 0.5rem 1rem;
min-width: 100px;
box-shadow: 0 0 5px var(--primary-color);
position: relative;
overflow: hidden;
}
.score-box::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(45deg, transparent, rgba(0, 247, 255, 0.1), transparent);
z-index: -1;
}
.score-title {
font-size: 0.8rem;
color: var(--secondary-color);
text-transform: uppercase;
letter-spacing: 1px;
}
.score-value {
font-size: 1.5rem;
font-weight: bold;
color: var(--primary-color);
}
.game-container {
position: relative;
width: 100%;
max-width: 500px;
padding: 20px;
}
.grid-container {
background: var(--grid-bg);
border-radius: 10px;
padding: 15px;
position: relative;
overflow: hidden;
box-shadow: 0 0 20px rgba(0, 247, 255, 0.2);
}
.grid-container::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
linear-gradient(45deg, transparent 48%, var(--primary-color) 49%, var(--primary-color) 51%, transparent 52%),
linear-gradient(-45deg, transparent 48%, var(--primary-color) 49%, var(--primary-color) 51%, transparent 52%);
background-size: 20px 20px;
opacity: 0.1;
z-index: -1;
}
.grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(4, 1fr);
gap: 15px;
width: 100%;
aspect-ratio: 1/1;
position: relative;
}
.grid-cell {
background: var(--tile-bg);
border-radius: 5px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
border: 1px solid rgba(0, 247, 255, 0.2);
}
.grid-cell::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, transparent, rgba(0, 247, 255, 0.05), transparent);
}
.tile {
position: absolute;
width: calc(25% - 15px);
height: calc(25% - 15px);
border-radius: 5px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.8rem;
font-weight: bold;
transition: all 0.15s ease-out;
z-index: 10;
background: var(--tile-bg);
border: 1px solid;
animation: appear 0.2s ease-out;
}
@keyframes appear {
0% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 1;
transform: scale(1);
}
}
.tile-2 {
color: #e0e0ff;
border-color: rgba(224, 224, 255, 0.3);
box-shadow: 0 0 5px rgba(224, 224, 255, 0.5);
}
.tile-4 {
color: #a0a0ff;
border-color: rgba(160, 160, 255, 0.4);
box-shadow: 0 0 8px rgba(160, 160, 255, 0.6);
}
.tile-8 {
color: #8080ff;
border-color: rgba(128, 128, 255, 0.5);
box-shadow: 0 0 10px rgba(128, 128, 255, 0.7);
background: rgba(30, 30, 80, 0.6);
}
.tile-16 {
color: #6060ff;
border-color: rgba(96, 96, 255, 0.6);
box-shadow: 0 0 12px rgba(96, 96, 255, 0.8);
background: rgba(30, 30, 100, 0.6);
}
.tile-32 {
color: #4040ff;
border-color: rgba(64, 64, 255, 0.7);
box-shadow: 0 0 14px rgba(64, 64, 255, 0.9);
background: rgba(30, 30, 120, 0.6);
}
.tile-64 {
color: #2020ff;
border-color: rgba(32, 32, 255, 0.8);
box-shadow: 0 0 16px rgba(32, 32, 255, 1);
background: rgba(30, 30, 140, 0.6);
}
.tile-128 {
color: var(--primary-color);
border-color: rgba(0, 247, 255, 0.7);
box-shadow: var(--glow);
background: rgba(30, 60, 150, 0.6);
font-size: 1.6rem;
}
.tile-256 {
color: var(--primary-color);
border-color: rgba(0, 247, 255, 0.8);
box-shadow: var(--glow);
background: rgba(30, 80, 160, 0.6);
font-size: 1.6rem;
}
.tile-512 {
color: var(--primary-color);
border-color: rgba(0, 247, 255, 0.9);
box-shadow: var(--glow);
background: rgba(30, 100, 170, 0.6);
font-size: 1.6rem;
}
.tile-1024 {
color: var(--primary-color);
border-color: rgba(0, 247, 255, 1);
box-shadow: var(--glow);
background: rgba(30, 120, 180, 0.6);
font-size: 1.4rem;
}
.tile-2048 {
color: var(--primary-color);
border-color: rgba(0, 247, 255, 1);
box-shadow: var(--glow), var(--glow-secondary);
background: rgba(30, 150, 200, 0.6);
font-size: 1.4rem;
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% {
box-shadow: var(--glow), var(--glow-secondary);
}
50% {
box-shadow: 0 0 20px var(--primary-color), 0 0 40px var(--primary-color), 0 0 20px var(--secondary-color), 0 0 40px var(--secondary-color);
}
100% {
box-shadow: var(--glow), var(--glow-secondary);
}
}
.tile-super {
color: var(--secondary-color);
border-color: rgba(255, 0, 247, 0.7);
box-shadow: var(--glow-secondary);
background: rgba(150, 30, 150, 0.6);
font-size: 1.2rem;
}
.merge-effect {
position: absolute;
width: calc(25% - 15px);
height: calc(25% - 15px);
border-radius: 5px;
background: radial-gradient(circle, var(--primary-color), transparent 70%);
opacity: 0;
z-index: 5;
animation: mergePulse 0.4s ease-out;
}
@keyframes mergePulse {
0% {
transform: scale(0.5);
opacity: 0.8;
}
100% {
transform: scale(1.5);
opacity: 0;
}
}
.game-message {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: none;
flex-direction: column;
align-items: center;
justify-content: center;
background: rgba(10, 10, 32, 0.9);
border-radius: 10px;
z-index: 100;
animation: fadeIn 0.3s ease-out;
}
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.game-message.game-won {
background: rgba(10, 10, 32, 0.95);
}
.game-message.game-over {
background: rgba(32, 10, 10, 0.95);
}
.game-message p {
font-size: 3rem;
font-weight: bold;
margin-bottom: 2rem;
text-align: center;
text-transform: uppercase;
}
.game-won p {
color: var(--primary-color);
text-shadow: var(--glow);
}
.game-over p {
color: #ff4040;
text-shadow: 0 0 10px #ff4040, 0 0 20px #ff4040;
}
.btn {
background: var(--tile-bg);
border: 1px solid var(--primary-color);
color: var(--primary-color);
padding: 0.8rem 1.5rem;
font-family: 'Orbitron', sans-serif;
font-size: 1rem;
border-radius: 5px;
cursor: pointer;
transition: all 0.2s;
text-transform: uppercase;
letter-spacing: 1px;
box-shadow: 0 0 5px var(--primary-color);
}
.btn:hover {
background: rgba(0, 247, 255, 0.1);
box-shadow: 0 0 10px var(--primary-color);
}
.btn:active {
transform: scale(0.98);
}
.instructions {
margin-top: 2rem;
text-align: center;
max-width: 500px;
padding: 0 20px;
color: rgba(224, 224, 255, 0.7);
font-size: 0.9rem;
line-height: 1.5;
}
.instructions strong {
color: var(--primary-color);
}
.particles {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: -1;
}
.particle {
position: absolute;
background: var(--primary-color);
border-radius: 50%;
pointer-events: none;
opacity: 0.5;
}
@media (max-width: 600px) {
h1 {
font-size: 2rem;
}
.tile {
font-size: 1.4rem;
}
.tile-128, .tile-256, .tile-512 {
font-size: 1.2rem;
}
.tile-1024, .tile-2048 {
font-size: 1rem;
}
.grid {
gap: 10px;
}
}
</style>
</head>
<body>
<div class="particles" id="particles"></div>
<div class="header">
<h1>Neon 2048</h1>
<div class="scores">
<div class="score-box">
<div class="score-title">Score</div>
<div class="score-value" id="score">0</div>
</div>
<div class="score-box">
<div class="score-title">Best</div>
<div class="score-value" id="best-score">0</div>
</div>
</div>
</div>
<div class="game-container">
<div class="grid-container">
<div class="grid" id="grid">
<!-- Grid cells will be generated by JavaScript -->
</div>
<div class="game-message" id="game-message">
<p id="message-text">You Win!</p>
<button class="btn" id="restart-btn">Play Again</button>
</div>
</div>
</div>
<div class="instructions">
<p><strong>HOW TO PLAY:</strong> Use your arrow keys or swipe to move the tiles. When two tiles with the same number touch, they merge into one!</p>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const grid = document.getElementById('grid');
const scoreDisplay = document.getElementById('score');
const bestScoreDisplay = document.getElementById('best-score');
const gameMessage = document.getElementById('game-message');
const messageText = document.getElementById('message-text');
const restartBtn = document.getElementById('restart-btn');
const particlesContainer = document.getElementById('particles');
let board = [];
let score = 0;
let bestScore = localStorage.getItem('bestScore') || 0;
let isGameOver = false;
let isGameWon = false;
let touchStartX = 0;
let touchStartY = 0;
let touchEndX = 0;
let touchEndY = 0;
bestScoreDisplay.textContent = bestScore;
// Initialize the game
function initGame() {
// Create grid cells
grid.innerHTML = '';
for (let i = 0; i < 16; i++) {
const cell = document.createElement('div');
cell.classList.add('grid-cell');
grid.appendChild(cell);
}
// Initialize board
board = [
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
];
score = 0;
scoreDisplay.textContent = score;
isGameOver = false;
isGameWon = false;
gameMessage.style.display = 'none';
gameMessage.classList.remove('game-won', 'game-over');
// Add two initial tiles
addRandomTile();
addRandomTile();
updateView();
}
// Add a random tile (2 or 4) to an empty cell
function addRandomTile() {
const emptyCells = [];
for (let r = 0; r < 4; r++) {
for (let c = 0; c < 4; c++) {
if (board[r][c] === 0) {
emptyCells.push({ r, c });
}
}
}
if (emptyCells.length > 0) {
const randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)];
board[randomCell.r][randomCell.c] = Math.random() < 0.9 ? 2 : 4;
// Animation for new tile
const tileElement = createTileElement(randomCell.r, randomCell.c, board[randomCell.r][randomCell.c]);
tileElement.style.animation = 'appear 0.2s ease-out';
grid.appendChild(tileElement);
}
}
// Create a tile element
function createTileElement(row, col, value) {
const tile = document.createElement('div');
tile.classList.add('tile', `tile-${value}`);
if (value > 2048) tile.classList.add('tile-super');
tile.textContent = value;
tile.id = `tile-${row}-${col}`;
updateTilePosition(tile, row, col);
return tile;
}
// Update tile position
function updateTilePosition(tile, row, col) {
const cellSize = grid.offsetWidth / 4;
tile.style.left = `${col * cellSize + 15}px`;
tile.style.top = `${row * cellSize + 15}px`;
}
// Update the view based on the board state
function updateView() {
// Remove all existing tiles
document.querySelectorAll('.tile').forEach(tile => tile.remove());
// Create tiles for non-zero cells
for (let r = 0; r < 4; r++) {
for (let c = 0; c < 4; c++) {
if (board[r][c] !== 0) {
const tile = createTileElement(r, c, board[r][c]);
grid.appendChild(tile);
}
}
}
}
// Move tiles left
function moveLeft() {
let moved = false;
for (let r = 0; r < 4; r++) {
// Remove zeros and compact to the left
let row = board[r].filter(val => val !== 0);
const zeros = Array(4 - row.length).fill(0);
row = row.concat(zeros);
// Check if the row changed
if (JSON.stringify(row) !== JSON.stringify(board[r])) {
moved = true;
board[r] = row;
}
// Merge adjacent equal tiles
for (let c = 0; c < 3; c++) {
if (board[r][c] !== 0 && board[r][c] === board[r][c + 1]) {
board[r][c] *= 2;
board[r][c + 1] = 0;
score += board[r][c];
moved = true;
// Create merge effect
createMergeEffect(r, c);
// Update best score if needed
if (score > bestScore) {
bestScore = score;
localStorage.setItem('bestScore', bestScore);
bestScoreDisplay.textContent = bestScore;
}
// Check for win condition
if (board[r][c] === 2048 && !isGameWon) {
gameWon();
}
// Compact again after merging
row = board[r].filter(val => val !== 0);
const newZeros = Array(4 - row.length).fill(0);
board[r] = row.concat(newZeros);
}
}
}
return moved;
}
// Move tiles right
function moveRight() {
let moved = false;
for (let r = 0; r < 4; r++) {
// Remove zeros and compact to the right
let row = board[r].filter(val => val !== 0);
const zeros = Array(4 - row.length).fill(0);
row = zeros.concat(row);
// Check if the row changed
if (JSON.stringify(row) !== JSON.stringify(board[r])) {
moved = true;
board[r] = row;
}
// Merge adjacent equal tiles
for (let c = 3; c > 0; c--) {
if (board[r][c] !== 0 && board[r][c] === board[r][c - 1]) {
board[r][c] *= 2;
board[r][c - 1] = 0;
score += board[r][c];
moved = true;
// Create merge effect
createMergeEffect(r, c);
// Update best score if needed
if (score > bestScore) {
bestScore = score;
localStorage.setItem('bestScore', bestScore);
bestScoreDisplay.textContent = bestScore;
}
// Check for win condition
if (board[r][c] === 2048 && !isGameWon) {
gameWon();
}
// Compact again after merging
row = board[r].filter(val => val !== 0);
const newZeros = Array(4 - row.length).fill(0);
board[r] = newZeros.concat(row);
}
}
}
return moved;
}
// Move tiles up
function moveUp() {
let moved = false;
for (let c = 0; c < 4; c++) {
// Get column values
let column = [board[0][c], board[1][c], board[2][c], board[3][c]];
// Remove zeros and compact up
let newColumn = column.filter(val => val !== 0);
const zeros = Array(4 - newColumn.length).fill(0);
newColumn = newColumn.concat(zeros);
// Check if the column changed
if (JSON.stringify(newColumn) !== JSON.stringify(column)) {
moved = true;
for (let r = 0; r < 4; r++) {
board[r][c] = newColumn[r];
}
}
// Merge adjacent equal tiles
for (let r = 0; r < 3; r++) {
if (board[r][c] !== 0 && board[r][c] === board[r + 1][c]) {
board[r][c] *= 2;
board[r + 1][c] = 0;
score += board[r][c];
moved = true;
// Create merge effect
createMergeEffect(r, c);
// Update best score if needed
if (score > bestScore) {
bestScore = score;
localStorage.setItem('bestScore', bestScore);
bestScoreDisplay.textContent = bestScore;
}
// Check for win condition
if (board[r][c] === 2048 && !isGameWon) {
gameWon();
}
// Compact again after merging
column = [board[0][c], board[1][c], board[2][c], board[3][c]];
newColumn = column.filter(val => val !== 0);
const newZeros = Array(4 - newColumn.length).fill(0);
newColumn = newColumn.concat(newZeros);
for (let r = 0; r < 4; r++) {
board[r][c] = newColumn[r];
}
}
}
}
return moved;
}
// Move tiles down
function moveDown() {
let moved = false;
for (let c = 0; c < 4; c++) {
// Get column values
let column = [board[0][c], board[1][c], board[2][c], board[3][c]];
// Remove zeros and compact down
let newColumn = column.filter(val => val !== 0);
const zeros = Array(4 - newColumn.length).fill(0);
newColumn = zeros.concat(newColumn);
// Check if the column changed
if (JSON.stringify(newColumn) !== JSON.stringify(column)) {
moved = true;
for (let r = 0; r < 4; r++) {
board[r][c] = newColumn[r];
}
}
// Merge adjacent equal tiles
for (let r = 3; r > 0; r--) {
if (board[r][c] !== 0 && board[r][c] === board[r - 1][c]) {
board[r][c] *= 2;
board[r - 1][c] = 0;
score += board[r][c];
moved = true;
// Create merge effect
createMergeEffect(r, c);
// Update best score if needed
if (score > bestScore) {
bestScore = score;
localStorage.setItem('bestScore', bestScore);
bestScoreDisplay.textContent = bestScore;
}
// Check for win condition
if (board[r][c] === 2048 && !isGameWon) {
gameWon();
}
// Compact again after merging
column = [board[0][c], board[1][c], board[2][c], board[3][c]];
newColumn = column.filter(val => val !== 0);
const newZeros = Array(4 - newColumn.length).fill(0);
newColumn = newZeros.concat(newColumn);
for (let r = 0; r < 4; r++) {
board[r][c] = newColumn[r];
}
}
}
}
return moved;
}
// Create merge effect animation
function createMergeEffect(row, col) {
const effect = document.createElement('div');
effect.classList.add('merge-effect');
updateTilePosition(effect, row, col);
grid.appendChild(effect);
// Create particles
createParticles(row, col);
// Remove effect after animation
setTimeout(() => {
effect.remove();
}, 400);
}
// Create particles animation
function createParticles(row, col) {
const cellSize = grid.offsetWidth / 4;
const x = col * cellSize + cellSize / 2;
const y = row * cellSize + cellSize / 2;
for (let i = 0; i < 10; i++) {
const particle = document.createElement('div');
particle.classList.add('particle');
const size = Math.random() * 5 + 2;
particle.style.width = `${size}px`;
particle.style.height = `${size}px`;
const angle = Math.random() * Math.PI * 2;
const distance = Math.random() * 30 + 20;
const duration = Math.random() * 0.5 + 0.3;
particle.style.left = `${x}px`;
particle.style.top = `${y}px`;
particle.style.opacity = '0.8';
particlesContainer.appendChild(particle);
setTimeout(() => {
particle.style.transition = `all ${duration}s ease-out`;
particle.style.transform = `translate(${Math.cos(angle) * distance}px, ${Math.sin(angle) * distance}px)`;
particle.style.opacity = '0';
}, 10);
setTimeout(() => {
particle.remove();
}, duration * 1000 + 100);
}
}
// Check if game is over
function checkGameOver() {
// Check if there are any empty cells
for (let r = 0; r < 4; r++) {
for (let c = 0; c < 4; c++) {
if (board[r][c] === 0) {
return false;
}
}
}
// Check if any adjacent tiles can be merged
for (let r = 0; r < 4; r++) {
for (let c = 0; c < 3; c++) {
if (board[r][c] === board[r][c + 1]) {
return false;
}
}
}
for (let c = 0; c < 4; c++) {
for (let r = 0; r < 3; r++) {
if (board[r][c] === board[r + 1][c]) {
return false;
}
}
}
return true;
}
// Game won
function gameWon() {
isGameWon = true;
gameMessage.classList.add('game-won');
messageText.textContent = 'You Win!';
gameMessage.style.display = 'flex';
}
// Game over
function gameOver() {
isGameOver = true;
gameMessage.classList.add('game-over');
messageText.textContent = 'Game Over!';
gameMessage.style.display = 'flex';
}
// Handle keyboard input
function handleKeyDown(e) {
if (isGameOver && !isGameWon) return;
let moved = false;
switch (e.key) {
case 'ArrowLeft':
moved = moveLeft();
break;
case 'ArrowRight':
moved = moveRight();
break;
case 'ArrowUp':
moved = moveUp();
break;
case 'ArrowDown':
moved = moveDown();
break;
default:
return;
}
if (moved) {
scoreDisplay.textContent = score;
addRandomTile();
updateView();
if (!isGameWon && checkGameOver()) {
gameOver();
}
}
}
// Handle touch events for mobile
function handleTouchStart(e) {
touchStartX = e.touches[0].clientX;
touchStartY = e.touches[0].clientY;
}
function handleTouchEnd(e) {
if (isGameOver && !isGameWon) return;
touchEndX = e.changedTouches[0].clientX;
touchEndY = e.changedTouches[0].clientY;
const dx = touchEndX - touchStartX;
const dy = touchEndY - touchStartY;
// Determine the direction of the swipe
if (Math.abs(dx) > Math.abs(dy)) {
// Horizontal swipe
if (dx > 50) {
// Right swipe
if (moveRight()) {
scoreDisplay.textContent = score;
addRandomTile();
updateView();
if (!isGameWon && checkGameOver()) {
gameOver();
}
}
} else if (dx < -50) {
// Left swipe
if (moveLeft()) {
scoreDisplay.textContent = score;
addRandomTile();
updateView();
if (!isGameWon && checkGameOver()) {
gameOver();
}
}
}
} else {
// Vertical swipe
if (dy > 50) {
// Down swipe
if (moveDown()) {
scoreDisplay.textContent = score;
addRandomTile();
updateView();
if (!isGameWon && checkGameOver()) {
gameOver();
}
}
} else if (dy < -50) {
// Up swipe
if (moveUp()) {
scoreDisplay.textContent = score;
addRandomTile();
updateView();
if (!isGameWon && checkGameOver()) {
gameOver();
}
}
}
}
}
// Restart game
restartBtn.addEventListener('click', initGame);
// Event listeners
document.addEventListener('keydown', handleKeyDown);
document.addEventListener('touchstart', handleTouchStart, false);
document.addEventListener('touchend', handleTouchEnd, false);
// Initialize the game
initGame();
// Create background particles
function createBackgroundParticles() {
const particleCount = 30;
for (let i = 0; i < particleCount; i++) {
const particle = document.createElement('div');
particle.classList.add('particle');
const size = Math.random() * 3 + 1;
particle.style.width = `${size}px`;
particle.style.height = `${size}px`;
// Random position
const x = Math.random() * 100;
const y = Math.random() * 100;
particle.style.left = `${x}%`;
particle.style.top = `${y}%`;
// Random opacity
particle.style.opacity = Math.random() * 0.5 + 0.1;
// Random animation
const duration = Math.random() * 20 + 10;
const delay = Math.random() * 5;
particle.style.animation = `float ${duration}s ease-in-out ${delay}s infinite`;
particlesContainer.appendChild(particle);
}
}
// Add floating animation
const style = document.createElement('style');
style.textContent = `
@keyframes float {
0%, 100% {
transform: translate(0, 0);
}
25% {
transform: translate(${Math.random() * 50 - 25}px, ${Math.random() * 50 - 25}px);
}
50% {
transform: translate(${Math.random() * 50 - 25}px, ${Math.random() * 50 - 25}px);
}
75% {
transform: translate(${Math.random() * 50 - 25}px, ${Math.random() * 50 - 25}px);
}
}
`;
document.head.appendChild(style);
createBackgroundParticles();
});
</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 <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body>
</html>