<!DOCTYPE html>
<html>
<head>
<title>Particle Collision Reaction</title>
<style>
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
background-color: #050505;
}
.collision-canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
filter: brightness(1.2);
}
</style>
</head>
<body>
<canvas class="collision-canvas"></canvas>
<script>
const canvas = document.querySelector('.collision-canvas');
const ctx = canvas.getContext('2d');
let width, height;
let particles = [];
let sparks = [];
function setSize() {
width = canvas.width = window.innerWidth;
height = canvas.height = window.innerHeight;
}
class Particle {
constructor(type) {
this.type = type; // 0 for left, 1 for right
this.reset();
}
reset(){
this.x = (this.type === 0) ? -5 : width + 5;
this.y = Math.random() * height;
this.vx = (this.type === 0) ? 2 + Math.random() * 2 : -2 - Math.random() * 2;
this.vy = Math.random() - 0.5;
this.hue = (this.type === 0) ? 180 : 330;
this.life = 100 + Math.random() * 100;
}
update(){
this.x += this.vx;
this.y += this.vy;
this.life--;
// Check collision zone and life
const distToCenter = Math.abs(this.x - width/2);
if (this.life <= 0 || (distToCenter < 50 && Math.random() > 0.8)) {
for(let i=0; i < 5; i++) {
sparks.push(new Spark(this.x, this.y, this.hue));
}
this.reset();
}
}
draw() {
ctx.fillStyle = `hsl(${this.hue}, 100%, 70%)`;
ctx.beginPath();
ctx.arc(this.x, this.y, 1.5, 0, Math.PI * 2);
ctx.fill();
}
}
class Spark {
constructor(x, y, hue){
this.x = x;
this.y = y;
const angle = Math.random() * Math.PI * 2;
const speed = Math.random() * 3 + 1;
this.vx = Math.cos(angle) * speed;
this.vy = Math.sin(angle) * speed;
this.life = 50;
this.hue = hue + Math.random()*40-20;
}
update(){
this.x += this.vx;
this.y += this.vy;
this.life--;
}
draw(){
const alpha = this.life / 50;
ctx.fillStyle = `hsla(${this.hue}, 100%, 80%, ${alpha})`;
ctx.beginPath();
ctx.arc(this.x, this.y, 1, 0, Math.PI * 2);
ctx.fill();
}
}
function init() {
setSize();
particles = [];
sparks = [];
for(let i=0; i<150; i++){
particles.push(new Particle(0));
particles.push(new Particle(1));
}
}
function animate(){
ctx.globalCompositeOperation = 'source-over';
ctx.fillStyle = 'rgba(0,0,0,0.1)';
ctx.fillRect(0,0,width,height);
ctx.globalCompositeOperation = 'lighter';
particles.forEach(p => { p.update(); p.draw(); });
for(let i=sparks.length-1; i>=0; i--){
sparks[i].update();
sparks[i].draw();
if(sparks[i].life <= 0) sparks.splice(i,1);
}
requestAnimationFrame(animate);
}
window.addEventListener('resize', init);
init();
animate();
</script>
</body>
</html>
<canvas class="collision-canvas"></canvas>
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
background-color: #050505;
}
.collision-canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
filter: brightness(1.2);
}
const canvas = document.querySelector('.collision-canvas');
const ctx = canvas.getContext('2d');
let width, height;
let particles = [];
let sparks = [];
function setSize() {
width = canvas.width = window.innerWidth;
height = canvas.height = window.innerHeight;
}
class Particle {
constructor(type) {
this.type = type; // 0 for left, 1 for right
this.reset();
}
reset(){
this.x = (this.type === 0) ? -5 : width + 5;
this.y = Math.random() * height;
this.vx = (this.type === 0) ? 2 + Math.random() * 2 : -2 - Math.random() * 2;
this.vy = Math.random() - 0.5;
this.hue = (this.type === 0) ? 180 : 330;
this.life = 100 + Math.random() * 100;
}
update(){
this.x += this.vx;
this.y += this.vy;
this.life--;
// Check collision zone and life
const distToCenter = Math.abs(this.x - width/2);
if (this.life <= 0 || (distToCenter < 50 && Math.random() > 0.8)) {
for(let i=0; i < 5; i++) {
sparks.push(new Spark(this.x, this.y, this.hue));
}
this.reset();
}
}
draw() {
ctx.fillStyle = `hsl(${this.hue}, 100%, 70%)`;
ctx.beginPath();
ctx.arc(this.x, this.y, 1.5, 0, Math.PI * 2);
ctx.fill();
}
}
class Spark {
constructor(x, y, hue){
this.x = x;
this.y = y;
const angle = Math.random() * Math.PI * 2;
const speed = Math.random() * 3 + 1;
this.vx = Math.cos(angle) * speed;
this.vy = Math.sin(angle) * speed;
this.life = 50;
this.hue = hue + Math.random()*40-20;
}
update(){
this.x += this.vx;
this.y += this.vy;
this.life--;
}
draw(){
const alpha = this.life / 50;
ctx.fillStyle = `hsla(${this.hue}, 100%, 80%, ${alpha})`;
ctx.beginPath();
ctx.arc(this.x, this.y, 1, 0, Math.PI * 2);
ctx.fill();
}
}
function init() {
setSize();
particles = [];
sparks = [];
for(let i=0; i<150; i++){
particles.push(new Particle(0));
particles.push(new Particle(1));
}
}
function animate(){
ctx.globalCompositeOperation = 'source-over';
ctx.fillStyle = 'rgba(0,0,0,0.1)';
ctx.fillRect(0,0,width,height);
ctx.globalCompositeOperation = 'lighter';
particles.forEach(p => { p.update(); p.draw(); });
for(let i=sparks.length-1; i>=0; i--){
sparks[i].update();
sparks[i].draw();
if(sparks[i].life <= 0) sparks.splice(i,1);
}
requestAnimationFrame(animate);
}
window.addEventListener('resize', init);
init();
animate();