forChip / index.html
NightFury2710's picture
Update index.html
2b75952 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Happy Birthday! 🎉</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Comic Sans MS', cursive, sans-serif;
overflow: hidden;
background: radial-gradient(ellipse at center, #1a1f4d 0%, #0a0e27 50%, #000000 100%);
height: 100vh;
height: 100svh;
display: flex;
justify-content: center;
align-items: center;
position: relative;
animation: backgroundShimmer 8s ease-in-out infinite;
}
@keyframes backgroundShimmer {
0%, 100% {
background: radial-gradient(ellipse at center, #1a1f4d 0%, #0a0e27 50%, #000000 100%);
}
50% {
background: radial-gradient(ellipse at center, #2d3561 0%, #141a3f 50%, #0a0e27 100%);
}
}
/* Bầu trời đầy sao */
.star {
position: absolute;
background: white;
border-radius: 50%;
animation: twinkle 2s ease-in-out infinite;
box-shadow: 0 0 6px rgba(255, 255, 255, 0.8);
}
@keyframes twinkle {
0%, 100% {
opacity: 0.2;
transform: scale(1);
box-shadow: 0 0 6px rgba(255, 255, 255, 0.8);
}
50% {
opacity: 1;
transform: scale(1.5);
box-shadow: 0 0 15px rgba(255, 255, 255, 1), 0 0 25px rgba(200, 220, 255, 0.6);
}
}
/* Thêm shooting stars */
.shooting-star {
position: absolute;
width: 2px;
height: 2px;
background: white;
border-radius: 50%;
box-shadow: 0 0 10px 2px white;
animation: shoot 3s ease-out infinite;
}
@keyframes shoot {
0% {
transform: translateX(0) translateY(0);
opacity: 1;
}
100% {
transform: translateX(300px) translateY(300px);
opacity: 0;
}
}
/* Hoa đăng bay thẳng lên trời (glowing CSS lanterns) */
.lantern {
position: absolute;
width: 56px;
height: 72px;
animation: floatUpStraight var(--float-duration, 12s) linear infinite, swayX var(--sway-duration, 3s) ease-in-out infinite;
filter: drop-shadow(0 0 20px rgba(255, 175, 0, 0.8));
will-change: transform, margin-left;
}
.lantern::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100%;
height: 100%;
border-radius: 26px / 34px;
background: radial-gradient(ellipse at 50% 70%, rgba(255, 245, 200, 0.95) 0 12%, #ffd66b 28%, #ff9f3b 52%, #ff7a2e 66%, #d4531b 78%, #86290a 100%);
box-shadow: 0 0 24px rgba(255, 180, 0, 0.9), 0 0 60px rgba(255, 120, 0, 0.45);
animation: lanternGlow 2s ease-in-out infinite;
}
.lantern::after {
content: '';
position: absolute;
left: 50%;
transform: translateX(-50%);
top: 6%;
width: 60%;
height: 10%;
background: #151515;
border-radius: 2px 2px 8px 8px;
opacity: 0.85;
box-shadow: 0 2px 4px rgba(0,0,0,0.4);
}
@keyframes lanternGlow {
0%, 100% {
filter: brightness(1) drop-shadow(0 0 10px rgba(255, 215, 0, 0.6));
}
50% {
filter: brightness(1.5) drop-shadow(0 0 25px rgba(255, 165, 0, 1));
}
}
@keyframes floatUpStraight {
0% {
transform: translateY(110vh) scale(var(--scale, 0.8));
opacity: 0;
}
5% {
opacity: 1;
}
95% {
opacity: 1;
}
100% {
transform: translateY(-10vh) scale(calc(var(--scale, 0.8) + 0.4));
opacity: 0;
}
}
@keyframes swayX {
0%, 100% { margin-left: -10px; }
50% { margin-left: 10px; }
}
/* Thiệp */
.card {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.95), rgba(255, 250, 220, 0.95));
border-radius: 40px;
padding: 50px 40px;
box-shadow: 0 30px 80px rgba(255, 215, 0, 0.6), 0 0 100px rgba(255, 165, 0, 0.4);
text-align: center;
max-width: 600px;
position: relative;
animation: cardEntrance 1.5s ease-out, cardGlow 3s ease-in-out infinite;
border: 3px solid rgba(255, 215, 0, 0.5);
z-index: 2;
}
@keyframes cardEntrance {
from {
transform: scale(0) rotate(180deg);
opacity: 0;
}
to {
transform: scale(1) rotate(0deg);
opacity: 1;
}
}
@keyframes cardGlow {
0%, 100% {
box-shadow: 0 30px 80px rgba(255, 215, 0, 0.6), 0 0 100px rgba(255, 165, 0, 0.4);
}
50% {
box-shadow: 0 30px 100px rgba(255, 215, 0, 0.9), 0 0 150px rgba(255, 140, 0, 0.6);
}
}
/* Họa tiết mặt trời ở trên */
.sun-decoration {
position: absolute;
top: -30px;
left: 50%;
transform: translateX(-50%);
width: 100px;
height: 100px;
animation: rotateSun 10s linear infinite;
}
.sun-center {
width: 60px;
height: 60px;
background: radial-gradient(circle, #ffd700, #ff8c00);
border-radius: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
box-shadow: 0 0 30px #ffd700, 0 0 60px #ff8c00;
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { transform: translate(-50%, -50%) scale(1); }
50% { transform: translate(-50%, -50%) scale(1.2); }
}
.sun-ray {
position: absolute;
width: 4px;
height: 25px;
background: linear-gradient(to bottom, #ffd700, transparent);
top: 0;
left: 50%;
transform-origin: bottom center;
}
@keyframes rotateSun {
from { transform: translateX(-50%) rotate(0deg); }
to { transform: translateX(-50%) rotate(360deg); }
}
.title {
font-size: 52px;
font-weight: bold;
background: linear-gradient(45deg, #ff1744, #ff9100, #ffd700, #00e676, #00b0ff, #d500f9);
background-size: 400% 400%;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
animation: gradientShift 3s ease infinite;
margin-bottom: 20px;
text-shadow: 0 0 20px rgba(255, 215, 0, 0.5);
filter: drop-shadow(0 0 10px rgba(255, 215, 0, 0.8));
}
@keyframes gradientShift {
0%, 100% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
}
/* Biển hoa hướng dương */
.sunflower-field {
display: flex;
justify-content: center;
gap: 8px;
margin: 20px 0;
flex-wrap: wrap;
animation: fadeIn 2s ease-in;
}
.sunflower {
font-size: 50px;
animation: swayFlower 3s ease-in-out infinite;
display: inline-block;
filter: drop-shadow(0 0 10px rgba(255, 215, 0, 0.6));
}
.sunflower:nth-child(even) {
animation-delay: 1s;
}
@keyframes swayFlower {
0%, 100% { transform: rotate(-5deg); }
50% { transform: rotate(5deg); }
}
.message {
font-size: 24px;
color: #d35400;
margin: 20px 0;
animation: fadeIn 2.5s ease-in;
font-weight: bold;
text-shadow: 2px 2px 4px rgba(255, 215, 0, 0.3);
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.sparkle {
position: absolute;
font-size: 30px;
animation: sparkleAnim 1.5s ease-in-out infinite;
}
@keyframes sparkleAnim {
0%, 100% {
opacity: 0;
transform: scale(0) rotate(0deg);
}
50% {
opacity: 1;
transform: scale(1.5) rotate(180deg);
}
}
.wish {
font-size: 20px;
color: #e67e22;
margin-top: 20px;
font-style: italic;
font-weight: bold;
animation: fadeIn 3s ease-in;
}
.hearts {
display: flex;
justify-content: center;
gap: 15px;
margin-top: 25px;
font-size: 40px;
}
.heart {
animation: heartbeat 1.5s ease-in-out infinite;
display: inline-block;
filter: drop-shadow(0 0 10px rgba(255, 0, 100, 0.6));
}
.heart:nth-child(2) {
animation-delay: 0.3s;
}
.heart:nth-child(3) {
animation-delay: 0.6s;
}
@keyframes heartbeat {
0%, 100% {
transform: scale(1);
}
25% {
transform: scale(1.4);
}
50% {
transform: scale(1);
}
}
/* Hiệu ứng ánh sáng xung quanh thiệp */
.glow-particle {
position: absolute;
border-radius: 50%;
background: radial-gradient(circle, rgba(255, 215, 0, 0.8), transparent);
animation: floatGlow 4s ease-in-out infinite;
}
/* Bảo đảm các hiệu ứng nổi ở trên video nền */
.star, .shooting-star, .lantern, .sparkle, .glow-particle { z-index: 2; }
@keyframes floatGlow {
0%, 100% {
transform: translate(0, 0) scale(1);
opacity: 0.6;
}
50% {
transform: translate(20px, -20px) scale(1.5);
opacity: 1;
}
}
/* Canvas cho pháo hoa */
#fireworksCanvas {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 1000;
}
/* Video nền toàn màn hình */
#bgVideo {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
z-index: 0;
pointer-events: none;
}
body {
cursor: crosshair;
}
/* Responsive adjustments for phones */
@media (max-width: 480px) {
.card {
padding: 24px 20px;
border-radius: 24px;
max-width: calc(100vw - 24px);
}
.title {
font-size: 36px;
margin-bottom: 12px;
}
.sunflower-field {
margin: 12px 0;
gap: 6px;
}
.sunflower {
font-size: 36px;
}
.message {
font-size: 18px;
margin: 12px 0;
}
.hearts {
font-size: 32px;
margin-top: 16px;
gap: 10px;
}
.wish {
font-size: 16px;
}
.sun-decoration {
width: 72px;
height: 72px;
top: -20px;
}
.sun-center {
width: 44px;
height: 44px;
}
.sun-ray {
height: 18px;
}
}
@media (max-height: 600px) {
.card {
padding: 20px 18px;
}
.sunflower-field {
margin: 10px 0;
}
}
</style>
</head>
<body>
<!-- Video nền -->
<video id="bgVideo" autoplay muted playsinline loop poster="bg_poster.png">
<source src="bg.mp4" type="video/mp4">
</video>
<div id="bgImageFallback" style="position:fixed;inset:0;background:url('bg_poster.png') center/cover no-repeat;z-index:-1;display:none;"></div>
<div class="card">
<!-- Mặt trời trang trí -->
<div class="sun-decoration">
<div class="sun-center"></div>
<div class="sun-ray" style="transform: rotate(0deg) translateX(-50%);"></div>
<div class="sun-ray" style="transform: rotate(45deg) translateX(-50%);"></div>
<div class="sun-ray" style="transform: rotate(90deg) translateX(-50%);"></div>
<div class="sun-ray" style="transform: rotate(135deg) translateX(-50%);"></div>
<div class="sun-ray" style="transform: rotate(180deg) translateX(-50%);"></div>
<div class="sun-ray" style="transform: rotate(225deg) translateX(-50%);"></div>
<div class="sun-ray" style="transform: rotate(270deg) translateX(-50%);"></div>
<div class="sun-ray" style="transform: rotate(315deg) translateX(-50%);"></div>
</div>
<!-- Hiệu ứng lấp lánh -->
<div class="sparkle" style="top: 60px; left: 30px;"></div>
<div class="sparkle" style="top: 70px; right: 40px; animation-delay: 0.5s;"></div>
<div class="sparkle" style="top: 100px; left: 60px; animation-delay: 1s;">💫</div>
<div class="sparkle" style="top: 100px; right: 60px; animation-delay: 1.5s;"></div>
<div class="sparkle" style="bottom: 100px; left: 50px; animation-delay: 0.7s;"></div>
<div class="sparkle" style="bottom: 100px; right: 50px; animation-delay: 1.2s;">💫</div>
<h1 class="title">HAPPY BIRTHDAY!</h1>
<!-- Biển hoa hướng dương -->
<div class="sunflower-field">
<span class="sunflower">🌻</span>
<span class="sunflower">🌻</span>
<span class="sunflower">🌻</span>
<span class="sunflower">🌻</span>
<span class="sunflower">🌻</span>
<span class="sunflower">🌻</span>
<span class="sunflower">🌻</span>
<span class="sunflower">🌻</span>
<span class="sunflower">🌻</span>
<span class="sunflower">🌻</span>
<span class="sunflower">🌻</span>
<span class="sunflower">🌻</span>
</div>
<p class="message">Chúc mừng sinh nhật Nhật Tiếp! Chúc em luôn tràn đầy niềm vui và hạnh phúc!</p>
<div class="hearts">
<span class="heart">💖</span>
<span class="heart">💝</span>
<span class="heart">💕</span>
</div>
<p class="wish">Chúc mọi điều ước của em đều trở thành hiện thực! ✨🌟</p>
</div>
<!-- Canvas cho pháo hoa -->
<canvas id="fireworksCanvas"></canvas>
<!-- Nhạc nền -->
<audio id="bgMusic" src="justsayhello.mp3" preload="auto" loop></audio>
<script>
// Tạo ngàn ngôi sao trên bầu trời
function createStars() {
for (let i = 0; i < 300; i++) {
const star = document.createElement('div');
star.className = 'star';
const size = Math.random() * 4 + 1;
star.style.width = size + 'px';
star.style.height = size + 'px';
star.style.left = Math.random() * 100 + 'vw';
star.style.top = Math.random() * 100 + 'vh';
star.style.animationDelay = Math.random() * 3 + 's';
star.style.animationDuration = (Math.random() * 3 + 1) + 's';
document.body.appendChild(star);
}
}
// Tạo sao băng
function createShootingStar() {
const shootingStar = document.createElement('div');
shootingStar.className = 'shooting-star';
shootingStar.style.left = Math.random() * 50 + 'vw';
shootingStar.style.top = Math.random() * 30 + 'vh';
shootingStar.style.animationDelay = Math.random() * 2 + 's';
shootingStar.style.animationDuration = (Math.random() * 2 + 2) + 's';
document.body.appendChild(shootingStar);
setTimeout(() => shootingStar.remove(), 5000);
}
// Tạo nhiều hoa đăng bay thẳng lên
function createLantern() {
const lantern = document.createElement('div');
lantern.className = 'lantern';
const baseLeft = (Math.random() * 90 + 5);
lantern.style.left = baseLeft + 'vw';
lantern.style.setProperty('--sway-duration', (Math.random() * 2 + 2.5) + 's');
lantern.style.setProperty('--float-duration', (Math.random() * 6 + 10) + 's');
lantern.style.setProperty('--scale', (Math.random() * 0.6 + 0.7).toFixed(2));
lantern.style.animationDelay = (Math.random() * 2) + 's';
// Wind parameters (vw-based)
lantern.dataset.baseLeft = String(baseLeft);
lantern.dataset.offset = '0';
lantern.dataset.vx = (Math.random() * 0.8 - 0.4).toFixed(3); // constant drift vw/s
lantern.dataset.amp = (Math.random() * 3 + 1.5).toFixed(2); // sway amplitude vw
lantern.dataset.freq = (Math.random() * 0.25 + 0.15).toFixed(3); // sway frequency Hz
lantern.dataset.seed = (Math.random() * Math.PI * 2).toFixed(3); // gust phase seed
document.body.appendChild(lantern);
setTimeout(() => lantern.remove(), 18000);
}
// Wind-driven drift for lanterns
(function windLanterns(){
let last = performance.now();
function step(now){
const dt = Math.min(0.05, (now - last) / 1000); // clamp large jumps
last = now;
const nodes = document.querySelectorAll('.lantern');
nodes.forEach(el => {
const baseLeft = parseFloat(el.dataset.baseLeft || '50');
let offset = parseFloat(el.dataset.offset || '0');
const vx = parseFloat(el.dataset.vx || '0');
const amp = parseFloat(el.dataset.amp || '2');
const freq = parseFloat(el.dataset.freq || '0.2');
const seed = parseFloat(el.dataset.seed || '0');
// Global gusty wind component (vw/s), varies with time and per-lantern phase
const gust = Math.sin(now * 0.0012 + seed) * 0.6 + Math.sin(now * 0.0005 + seed * 1.7) * 0.4;
// Integrate horizontal speed
offset += (vx + gust) * dt;
// Gentle oscillation around the drift
const t = now / 1000;
const oscillation = Math.sin((t + seed) * (Math.PI * 2) * freq) * amp;
const left = baseLeft + offset + oscillation;
el.style.left = left + 'vw';
el.dataset.offset = offset.toFixed(3);
// Remove if far out of screen horizontally
if (left < -20 || left > 120) {
el.remove();
}
});
requestAnimationFrame(step);
}
requestAnimationFrame(step);
})();
// Bật nhạc khi có tương tác người dùng (tuân thủ autoplay policy)
let __bgAudioEnabled = false;
function tryEnableAudio() {
if (__bgAudioEnabled) return;
const audio = document.getElementById('bgMusic');
if (!audio) return;
audio.volume = 0.6;
audio.play().then(() => { __bgAudioEnabled = true; }).catch(() => {});
}
// Fallback hình nếu video không tải được hoặc không có file
(function setupVideoFallback(){
const video = document.getElementById('bgVideo');
const fallback = document.getElementById('bgImageFallback');
if (!video || !fallback) return;
let showedFallback = false;
function showFallback(){
if (showedFallback) return;
showedFallback = true;
fallback.style.display = 'block';
video.style.display = 'none';
}
video.addEventListener('error', showFallback, { once: true });
video.addEventListener('stalled', showFallback, { once: true });
video.addEventListener('abort', showFallback, { once: true });
// Nếu sau 2s không có enough data để phát, dùng fallback
setTimeout(() => {
const ready = video.readyState >= 2; // HAVE_CURRENT_DATA
if (!ready || isNaN(video.duration)) showFallback();
}, 2000);
})();
// Hệ thống pháo hoa Canvas mới
class FireworksSystem {
constructor() {
this.canvas = document.getElementById('fireworksCanvas');
this.ctx = this.canvas.getContext('2d');
this.fireworks = [];
this.particles = [];
this.counter = 0;
this.resize();
this.setupEventListeners();
// Tạo một vài pháo hoa ban đầu để hiển thị ngay
for (let i = 0; i < 3; i++) {
this.fireworks.push(new Firework(
this.random(this.spawnA, this.spawnB),
this.height,
this.random(0, this.width),
this.random(this.spawnC, this.spawnD),
this.random(0, 360),
this.random(30, 110)
));
}
this.animate();
}
resize() {
const dpr = Math.max(1, Math.min(2, window.devicePixelRatio || 1));
const cssWidth = window.innerWidth;
const cssHeight = window.innerHeight;
this.canvas.style.width = cssWidth + 'px';
this.canvas.style.height = cssHeight + 'px';
this.canvas.width = Math.floor(cssWidth * dpr);
this.canvas.height = Math.floor(cssHeight * dpr);
this.ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
this.width = cssWidth;
this.height = cssHeight;
// Vùng spawn pháo hoa
let center = this.width / 2;
this.spawnA = center - center / 4;
this.spawnB = center + center / 4;
this.spawnC = this.height * 0.1;
this.spawnD = this.height * 0.5;
}
setupEventListeners() {
window.addEventListener('resize', () => this.resize());
document.addEventListener('click', (e) => { tryEnableAudio(); this.onClick(e); });
document.addEventListener('touchstart', (e) => { tryEnableAudio(); this.onClick(e); });
}
onClick(event) {
let x = event.clientX || (event.touches && event.touches[0].pageX);
let y = event.clientY || (event.touches && event.touches[0].pageY);
let count = this.random(3, 5);
for (let i = 0; i < count; i++) {
this.fireworks.push(new Firework(
this.random(this.spawnA, this.spawnB),
this.height,
x,
y,
this.random(0, 360),
this.random(30, 110)
));
}
this.counter = -1;
}
random(min, max) {
return Math.random() * (max - min + 1) + min | 0;
}
update(delta) {
// Làm mờ vệt cũ mà không che thiệp (canvas trong suốt)
this.ctx.globalCompositeOperation = 'destination-out';
this.ctx.fillStyle = `rgba(0,0,0,${Math.min(0.3, 7 * delta)})`;
this.ctx.fillRect(0, 0, this.width, this.height);
this.ctx.globalCompositeOperation = 'lighter';
// Cập nhật pháo hoa
for (let firework of this.fireworks) {
firework.update(delta, this.ctx, this.fireworks);
}
// Tạo pháo hoa tự động
this.counter += delta * 3;
if (this.counter >= 1) {
this.fireworks.push(new Firework(
this.random(this.spawnA, this.spawnB),
this.height,
this.random(0, this.width),
this.random(this.spawnC, this.spawnD),
this.random(0, 360),
this.random(30, 110)
));
this.counter = 0;
}
// Dọn dẹp pháo hoa đã chết
if (this.fireworks.length > 1000) {
this.fireworks = this.fireworks.filter(firework => !firework.dead);
}
}
animate() {
let then = performance.now();
const loop = () => {
requestAnimationFrame(loop);
let now = performance.now();
let delta = (now - then) / 1000;
then = now;
this.update(delta);
};
loop();
}
}
class Firework {
constructor(x, y, targetX, targetY, shade, offsprings) {
this.dead = false;
this.offsprings = offsprings;
this.madeChilds = false;
this.x = x;
this.y = y;
this.targetX = targetX;
this.targetY = targetY;
this.shade = shade;
this.history = [];
}
update(delta, ctx, fireworks) {
if (this.dead) return;
let xDiff = this.targetX - this.x;
let yDiff = this.targetY - this.y;
if (Math.abs(xDiff) > 3 || Math.abs(yDiff) > 3) {
// Vẫn đang bay
this.x += xDiff * 2 * delta;
this.y += yDiff * 2 * delta;
this.history.push({
x: this.x,
y: this.y
});
if (this.history.length > 20) this.history.shift();
} else {
// Đã đến đích, tạo pháo hoa con
if (this.offsprings && !this.madeChilds) {
let babies = this.offsprings / 2;
for (let i = 0; i < babies; i++) {
let targetX = this.x + this.offsprings * Math.cos(Math.PI * 2 * i / babies);
let targetY = this.y + this.offsprings * Math.sin(Math.PI * 2 * i / babies);
fireworks.push(new Firework(this.x, this.y, targetX, targetY, this.shade, 0));
}
}
this.madeChilds = true;
this.history.shift();
}
if (this.history.length === 0) {
this.dead = true;
} else if (this.offsprings) {
// Vẽ trail
for (let i = 0; i < this.history.length; i++) {
let point = this.history[i];
ctx.beginPath();
ctx.fillStyle = `hsl(${this.shade},100%,${i}%)`;
ctx.arc(point.x, point.y, 1, 0, Math.PI * 2, false);
ctx.fill();
}
} else {
// Vẽ pháo hoa con
ctx.beginPath();
ctx.fillStyle = `hsl(${this.shade},100%,50%)`;
ctx.arc(this.x, this.y, 1, 0, Math.PI * 2, false);
ctx.fill();
}
}
}
// Tạo các hạt sáng lung linh xung quanh thiệp
function createGlowParticles() {
for (let i = 0; i < 30; i++) {
setTimeout(() => {
const particle = document.createElement('div');
particle.className = 'glow-particle';
const size = Math.random() * 40 + 15;
particle.style.width = size + 'px';
particle.style.height = size + 'px';
particle.style.left = Math.random() * 100 + 'vw';
particle.style.top = Math.random() * 100 + 'vh';
particle.style.animationDelay = Math.random() * 2 + 's';
particle.style.animationDuration = (Math.random() * 2 + 3) + 's';
document.body.appendChild(particle);
setTimeout(() => particle.remove(), 8000);
}, i * 80);
}
}
// Khởi tạo
createStars();
// Tạo hoa đăng liên tục - giảm xuống
setInterval(createLantern, 800);
// Tạo sao băng
setInterval(createShootingStar, 4000);
// Tạo các hạt sáng liên tục
createGlowParticles();
setInterval(createGlowParticles, 4000);
// Khởi tạo hệ thống pháo hoa Canvas
const fireworksSystem = new FireworksSystem();
</script>
</body>
</html>