<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let width, height;
let drops = [];
let splashes = [];
let ripples = [];
class Drop {
constructor(x) {
this.x = x || Math.random() * width;
this.y = -20;
this.speed = Math.random() * 2 + 1;
this.chars = this.generateChars();
this.size = Math.random() * 16 + 12;
this.opacity = 1;
this.wobble = Math.random() * 0.02 - 0.01;
this.wobbleSpeed = Math.random() * 0.05 + 0.02;
this.wobbleOffset = Math.random() * Math.PI * 2;
this.thickness = Math.random() * 0.5 + 0.5;
this.melted = false;
this.meltProgress = 0;
this.splashed = false;
this.trail = [];
this.maxTrailLength = 20;
}
generateChars() {
const length = Math.floor(Math.random() * 10) + 5;
let chars = '';
for (let i = 0; i < length; i++) {
chars += String.fromCharCode(0x30A0 + Math.random() * 96);
}
return chars;
}
update() {
// Store trail positions
this.trail.push({ x: this.x, y: this.y, opacity: this.opacity });
if (this.trail.length > this.maxTrailLength) {
this.trail.shift();
}
// Wobble effect
this.x += Math.sin(this.y * this.wobbleSpeed + this.wobbleOffset) * this.wobble;
// Accelerate as it falls
this.speed += 0.01;
this.y += this.speed;
// Melt effect
if (this.y > height * 0.7 && !this.melted) {
this.meltProgress += 0.02;
if (this.meltProgress > 1) {
this.melted = true;
}
}
// Splash effect
if (this.y >= height - 20 && !this.splashed) {
this.splashed = true;
this.createSplash();
this.createRipple();
return false;
}
return this.y < height + 50;
}
createSplash() {
for (let i = 0; i < 8; i++) {
splashes.push(new Splash(this.x, height, this.size));
}
}
createRipple() {
ripples.push(new Ripple(this.x, height));
}
draw() {
ctx.save();
// Draw trail
this.trail.forEach((point, index) => {
const trailOpacity = (index / this.trail.length) * 0.3;
ctx.globalAlpha = trailOpacity;
ctx.fillStyle = '#00ff00';
ctx.font = `${this.size * 0.8}px monospace`;
ctx.fillText(this.chars[index % this.chars.length], point.x, point.y);
});
// Main drop
ctx.globalAlpha = this.opacity;
// Chrome gradient
const gradient = ctx.createLinearGradient(this.x, this.y - this.size, this.x, this.y + this.size);
gradient.addColorStop(0, '#00ff00');
gradient.addColorStop(0.3, '#00cc00');
gradient.addColorStop(0.7, '#009900');
gradient.addColorStop(1, '#006600');
ctx.fillStyle = gradient;
// Metallic glow
ctx.shadowColor = '#00ff00';
ctx.shadowBlur = 20;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
// Draw with thickness
ctx.lineWidth = this.thickness;
ctx.strokeStyle = gradient;
ctx.beginPath();
if (this.melted) {
// Melted shape
const meltWidth = this.size * (1 + this.meltProgress * 2);
ctx.moveTo(this.x - meltWidth/2, this.y);
ctx.quadraticCurveTo(
this.x, this.y + this.size * this.meltProgress,
this.x + meltWidth/2, this.y
);
ctx.stroke();
} else {
// Normal drop shape
ctx.moveTo(this.x, this.y - this.size);
ctx.quadraticCurveTo(this.x, this.y, this.x - this.size/3, this.y + this.size);
ctx.quadraticCurveTo(this.x, this.y + this.size/2, this.x + this.size/3, this.y + this.size);
ctx.quadraticCurveTo(this.x, this.y, this.x, this.y - this.size);
ctx.fill();
}
// Inner glow
ctx.globalAlpha = 0.5;
ctx.fillStyle = '#00ff00';
ctx.fillRect(this.x - 1, this.y - this.size/2, 2, this.size);
ctx.restore();
}
}
class Splash {
constructor(x, y, size) {
this.x = x + (Math.random() - 0.5) * size;
this.y = y;
this.vx = (Math.random() - 0.5) * 4;
this.vy = -Math.random() * 6 - 2;
this.size = Math.random() * size/2 + 2;
this.life = 1;
this.decay = 0.02;
this.gravity = 0.2;
}
update() {
this.x += this.vx;
this.y += this.vy;
this.vy += this.gravity;
this.life -= this.decay;
return this.life > 0;
}
draw() {
ctx.save();
ctx.globalAlpha = this.life;
// Chrome splash particle
const gradient = ctx.createRadialGradient(this.x, this.y, 0, this.x, this.y, this.size);
gradient.addColorStop(0, '#00ff00');
gradient.addColorStop(0.5, '#00cc00');
gradient.addColorStop(1, 'rgba(0, 150, 0, 0)');
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
}
class Ripple {
constructor(x, y) {
this.x = x;
this.y = y;
this.radius = 0;
this.maxRadius = 100;
this.life = 1;
this.decay = 0.01;
}
update() {
this.radius += 3;
this.life -= this.decay;
return this.life > 0 && this.radius < this.maxRadius;
}
draw() {
ctx.save();
ctx.globalAlpha = this.life * 0.3;
ctx.strokeStyle = '#00ff00';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.stroke();
ctx.restore();
}
}
function resize() {
width = canvas.width = window.innerWidth;
height = canvas.height = window.innerHeight;
}
function animate() {
// Clear with slight trail effect
ctx.fillStyle = 'rgba(0, 0, 0, 0.05)';
ctx.fillRect(0, 0, width, height);
// Create new drops
if (Math.random() < 0.1) {
drops.push(new Drop());
}
// Update and draw drops
drops = drops.filter(drop => {
const alive = drop.update();
if (alive) drop.draw();
return alive;
});
// Update and draw splashes
splashes = splashes.filter(splash => {
const alive = splash.update();
if (alive) splash.draw();
return alive;
});
// Update and draw ripples
ripples = ripples.filter(ripple => {
const alive = ripple.update();
if (alive) ripple.draw();
return alive;
});
// Add distortion effect
ctx.save();
ctx.globalCompositeOperation = 'screen';
ctx.fillStyle = 'rgba(0, 255, 0, 0.02)';
ctx.fillRect(0, 0, width, height);
ctx.restore();
requestAnimationFrame(animate);
}
// Initialize
window.addEventListener('resize', resize);
resize();
// Start with some drops
for (let i = 0; i < 5; i++) {
drops.push(new Drop(Math.random() * width));
}
animate();
</script>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let width, height;
let drops = [];
let splashes = [];
let ripples = [];
class Drop {
constructor(x) {
this.x = x || Math.random() * width;
this.y = -20;
this.speed = Math.random() * 2 + 1;
this.chars = this.generateChars();
this.size = Math.random() * 16 + 12;
this.opacity = 1;
this.wobble = Math.random() * 0.02 - 0.01;
this.wobbleSpeed = Math.random() * 0.05 + 0.02;
this.wobbleOffset = Math.random() * Math.PI * 2;
this.thickness = Math.random() * 0.5 + 0.5;
this.melted = false;
this.meltProgress = 0;
this.splashed = false;
this.trail = [];
this.maxTrailLength = 20;
}
generateChars() {
const length = Math.floor(Math.random() * 10) + 5;
let chars = '';
for (let i = 0; i < length; i++) {
chars += String.fromCharCode(0x30A0 + Math.random() * 96);
}
return chars;
}
update() {
// Store trail positions
this.trail.push({ x: this.x, y: this.y, opacity: this.opacity });
if (this.trail.length > this.maxTrailLength) {
this.trail.shift();
}
// Wobble effect
this.x += Math.sin(this.y * this.wobbleSpeed + this.wobbleOffset) * this.wobble;
// Accelerate as it falls
this.speed += 0.01;
this.y += this.speed;
// Melt effect
if (this.y > height * 0.7 && !this.melted) {
this.meltProgress += 0.02;
if (this.meltProgress > 1) {
this.melted = true;
}
}
// Splash effect
if (this.y >= height - 20 && !this.splashed) {
this.splashed = true;
this.createSplash();
this.createRipple();
return false;
}
return this.y < height + 50;
}
createSplash() {
for (let i = 0; i < 8; i++) {
splashes.push(new Splash(this.x, height, this.size));
}
}
createRipple() {
ripples.push(new Ripple(this.x, height));
}
draw() {
ctx.save();
// Draw trail
this.trail.forEach((point, index) => {
const trailOpacity = (index / this.trail.length) * 0.3;
ctx.globalAlpha = trailOpacity;
ctx.fillStyle = '#00ff00';
ctx.font = `${this.size * 0.8}px monospace`;
ctx.fillText(this.chars[index % this.chars.length], point.x, point.y);
});
// Main drop
ctx.globalAlpha = this.opacity;
// Chrome gradient
const gradient = ctx.createLinearGradient(this.x, this.y - this.size, this.x, this.y + this.size);
gradient.addColorStop(0, '#00ff00');
gradient.addColorStop(0.3, '#00cc00');
gradient.addColorStop(0.7, '#009900');
gradient.addColorStop(1, '#006600');
ctx.fillStyle = gradient;
// Metallic glow
ctx.shadowColor = '#00ff00';
ctx.shadowBlur = 20;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
// Draw with thickness
ctx.lineWidth = this.thickness;
ctx.strokeStyle = gradient;
ctx.beginPath();
if (this.melted) {
// Melted shape
const meltWidth = this.size * (1 + this.meltProgress * 2);
ctx.moveTo(this.x - meltWidth/2, this.y);
ctx.quadraticCurveTo(
this.x, this.y + this.size * this.meltProgress,
this.x + meltWidth/2, this.y
);
ctx.stroke();
} else {
// Normal drop shape
ctx.moveTo(this.x, this.y - this.size);
ctx.quadraticCurveTo(this.x, this.y, this.x - this.size/3, this.y + this.size);
ctx.quadraticCurveTo(this.x, this.y + this.size/2, this.x + this.size/3, this.y + this.size);
ctx.quadraticCurveTo(this.x, this.y, this.x, this.y - this.size);
ctx.fill();
}
// Inner glow
ctx.globalAlpha = 0.5;
ctx.fillStyle = '#00ff00';
ctx.fillRect(this.x - 1, this.y - this.size/2, 2, this.size);
ctx.restore();
}
}
class Splash {
constructor(x, y, size) {
this.x = x + (Math.random() - 0.5) * size;
this.y = y;
this.vx = (Math.random() - 0.5) * 4;
this.vy = -Math.random() * 6 - 2;
this.size = Math.random() * size/2 + 2;
this.life = 1;
this.decay = 0.02;
this.gravity = 0.2;
}
update() {
this.x += this.vx;
this.y += this.vy;
this.vy += this.gravity;
this.life -= this.decay;
return this.life > 0;
}
draw() {
ctx.save();
ctx.globalAlpha = this.life;
// Chrome splash particle
const gradient = ctx.createRadialGradient(this.x, this.y, 0, this.x, this.y, this.size);
gradient.addColorStop(0, '#00ff00');
gradient.addColorStop(0.5, '#00cc00');
gradient.addColorStop(1, 'rgba(0, 150, 0, 0)');
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
}
class Ripple {
constructor(x, y) {
this.x = x;
this.y = y;
this.radius = 0;
this.maxRadius = 100;
this.life = 1;
this.decay = 0.01;
}
update() {
this.radius += 3;
this.life -= this.decay;
return this.life > 0 && this.radius < this.maxRadius;
}
draw() {
ctx.save();
ctx.globalAlpha = this.life * 0.3;
ctx.strokeStyle = '#00ff00';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.stroke();
ctx.restore();
}
}
function resize() {
width = canvas.width = window.innerWidth;
height = canvas.height = window.innerHeight;
}
function animate() {
// Clear with slight trail effect
ctx.fillStyle = 'rgba(0, 0, 0, 0.05)';
ctx.fillRect(0, 0, width, height);
// Create new drops
if (Math.random() < 0.1) {
drops.push(new Drop());
}
// Update and draw drops
drops = drops.filter(drop => {
const alive = drop.update();
if (alive) drop.draw();
return alive;
});
// Update and draw splashes
splashes = splashes.filter(splash => {
const alive = splash.update();
if (alive) splash.draw();
return alive;
});
// Update and draw ripples
ripples = ripples.filter(ripple => {
const alive = ripple.update();
if (alive) ripple.draw();
return alive;
});
// Add distortion effect
ctx.save();
ctx.globalCompositeOperation = 'screen';
ctx.fillStyle = 'rgba(0, 255, 0, 0.02)';
ctx.fillRect(0, 0, width, height);
ctx.restore();
requestAnimationFrame(animate);
}
// Initialize
window.addEventListener('resize', resize);
resize();
// Start with some drops
for (let i = 0; i < 5; i++) {
drops.push(new Drop(Math.random() * width));
}
animate();
</script>