233 lines
7.6 KiB
HTML
233 lines
7.6 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>BUBA PARTICLE FLEX 🔥</title>
|
|
<style>
|
|
body {
|
|
margin: 0;
|
|
overflow: hidden;
|
|
background: #000;
|
|
font-family: 'Courier New', monospace;
|
|
}
|
|
canvas {
|
|
display: block;
|
|
}
|
|
#controls {
|
|
position: absolute;
|
|
top: 20px;
|
|
left: 20px;
|
|
color: #0ff;
|
|
font-size: 14px;
|
|
text-shadow: 0 0 10px #0ff;
|
|
z-index: 10;
|
|
}
|
|
#message {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
font-size: 120px;
|
|
font-weight: bold;
|
|
color: rgba(255, 255, 255, 0.05);
|
|
pointer-events: none;
|
|
letter-spacing: 20px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="controls">
|
|
<div>🔥 BUBA PARTICLE SYSTEM 🔥</div>
|
|
<div>Click to spawn fire • Move mouse to attract • Press SPACE for chaos mode</div>
|
|
<div id="stats">Particles: 0 | FPS: 60</div>
|
|
</div>
|
|
<div id="message">BUBA</div>
|
|
<canvas id="canvas"></canvas>
|
|
|
|
<script>
|
|
const canvas = document.getElementById('canvas');
|
|
const ctx = canvas.getContext('2d');
|
|
canvas.width = window.innerWidth;
|
|
canvas.height = window.innerHeight;
|
|
|
|
let particles = [];
|
|
let mouse = { x: canvas.width / 2, y: canvas.height / 2 };
|
|
let chaosMode = false;
|
|
let mouseDown = false;
|
|
let frameCount = 0;
|
|
let lastTime = performance.now();
|
|
|
|
class Particle {
|
|
constructor(x, y, vx, vy, hue) {
|
|
this.x = x;
|
|
this.y = y;
|
|
this.vx = vx || (Math.random() - 0.5) * 8;
|
|
this.vy = vy || (Math.random() - 0.5) * 8;
|
|
this.life = 1;
|
|
this.decay = 0.003 + Math.random() * 0.003;
|
|
this.size = 2 + Math.random() * 3;
|
|
this.hue = hue !== undefined ? hue : Math.random() * 60;
|
|
this.gravity = -0.05;
|
|
}
|
|
|
|
update() {
|
|
// Attract to mouse
|
|
const dx = mouse.x - this.x;
|
|
const dy = mouse.y - this.y;
|
|
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
|
|
if (dist < 200 && !chaosMode) {
|
|
this.vx += (dx / dist) * 0.3;
|
|
this.vy += (dy / dist) * 0.3;
|
|
}
|
|
|
|
if (chaosMode) {
|
|
this.vx += (Math.random() - 0.5) * 2;
|
|
this.vy += (Math.random() - 0.5) * 2;
|
|
}
|
|
|
|
this.vy += this.gravity;
|
|
this.x += this.vx;
|
|
this.y += this.vy;
|
|
this.life -= this.decay;
|
|
this.vx *= 0.99;
|
|
this.vy *= 0.99;
|
|
|
|
return this.life > 0 && this.x > -50 && this.x < canvas.width + 50 && this.y > -50 && this.y < canvas.height + 50;
|
|
}
|
|
|
|
draw() {
|
|
const alpha = this.life;
|
|
const saturation = 100;
|
|
const lightness = 50 + this.life * 30;
|
|
|
|
ctx.shadowBlur = 20;
|
|
ctx.shadowColor = `hsla(${this.hue}, ${saturation}%, ${lightness}%, ${alpha})`;
|
|
|
|
ctx.fillStyle = `hsla(${this.hue}, ${saturation}%, ${lightness}%, ${alpha})`;
|
|
ctx.beginPath();
|
|
ctx.arc(this.x, this.y, this.size * this.life, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
}
|
|
}
|
|
|
|
function spawnParticles(x, y, count = 50, hue) {
|
|
for (let i = 0; i < count; i++) {
|
|
const angle = (Math.PI * 2 * i) / count;
|
|
const speed = 2 + Math.random() * 4;
|
|
particles.push(new Particle(
|
|
x,
|
|
y,
|
|
Math.cos(angle) * speed,
|
|
Math.sin(angle) * speed,
|
|
hue
|
|
));
|
|
}
|
|
}
|
|
|
|
function spawnText(text, x, y) {
|
|
ctx.font = 'bold 100px Arial';
|
|
ctx.fillStyle = 'white';
|
|
ctx.fillText(text, x, y);
|
|
|
|
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
const step = 4;
|
|
for (let px = 0; px < canvas.width; px += step) {
|
|
for (let py = 0; py < canvas.height; py += step) {
|
|
const i = (py * canvas.width + px) * 4;
|
|
if (imageData.data[i + 3] > 128) {
|
|
particles.push(new Particle(
|
|
px,
|
|
py,
|
|
(Math.random() - 0.5) * 2,
|
|
(Math.random() - 0.5) * 2,
|
|
Math.random() * 60
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function animate() {
|
|
ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
|
|
ctx.shadowBlur = 0;
|
|
|
|
particles = particles.filter(p => {
|
|
const alive = p.update();
|
|
if (alive) p.draw();
|
|
return alive;
|
|
});
|
|
|
|
if (mouseDown) {
|
|
spawnParticles(mouse.x, mouse.y, 5, frameCount % 360);
|
|
}
|
|
|
|
frameCount++;
|
|
|
|
// FPS counter
|
|
const now = performance.now();
|
|
if (now - lastTime > 1000) {
|
|
const fps = Math.round(frameCount / ((now - lastTime) / 1000));
|
|
document.getElementById('stats').textContent =
|
|
`Particles: ${particles.length} | FPS: ${fps}`;
|
|
frameCount = 0;
|
|
lastTime = now;
|
|
}
|
|
|
|
requestAnimationFrame(animate);
|
|
}
|
|
|
|
// Event listeners
|
|
canvas.addEventListener('mousemove', e => {
|
|
mouse.x = e.clientX;
|
|
mouse.y = e.clientY;
|
|
});
|
|
|
|
canvas.addEventListener('mousedown', e => {
|
|
mouseDown = true;
|
|
spawnParticles(e.clientX, e.clientY, 100, Math.random() * 360);
|
|
});
|
|
|
|
canvas.addEventListener('mouseup', () => {
|
|
mouseDown = false;
|
|
});
|
|
|
|
window.addEventListener('keydown', e => {
|
|
if (e.code === 'Space') {
|
|
e.preventDefault();
|
|
chaosMode = !chaosMode;
|
|
if (chaosMode) {
|
|
document.getElementById('message').textContent = 'CHAOS';
|
|
} else {
|
|
document.getElementById('message').textContent = 'BUBA';
|
|
}
|
|
}
|
|
|
|
if (e.code === 'Enter') {
|
|
spawnText('BUBA', canvas.width / 2 - 200, canvas.height / 2);
|
|
}
|
|
|
|
if (e.code === 'KeyE') {
|
|
spawnText('EPIC', canvas.width / 2 - 200, canvas.height / 2);
|
|
}
|
|
});
|
|
|
|
window.addEventListener('resize', () => {
|
|
canvas.width = window.innerWidth;
|
|
canvas.height = window.innerHeight;
|
|
});
|
|
|
|
// Auto-spawn intro
|
|
setTimeout(() => {
|
|
spawnText('BUBA', canvas.width / 2 - 200, canvas.height / 2);
|
|
}, 500);
|
|
|
|
animate();
|
|
</script>
|
|
</body>
|
|
</html>
|