Spaces:
Running
Running
// Original stars.js code modified with AI in the following order: | |
// 1) Microsoft Copilot | |
// 2) Deepseek AI | |
// 3) Qwen3 Coder | |
(() => { | |
const canvas = document.getElementById("starfield"); | |
const ctx = canvas.getContext("2d"); | |
let width, height; | |
let bgCanvas, bgCtx; | |
let starSprites = []; | |
let layers = []; | |
let comets = []; | |
let pulses = []; | |
let particles = []; | |
let mouse = { x: 0, y: 0, active: false }; | |
// Layer configurations: count, base speed, draw size | |
const layerConfigs = [ | |
{ count: 500, speed: 1.5, size: 2.5 }, | |
{ count: 400, speed: 3.0, size: 4.0 }, | |
{ count: 300, speed: 6.0, size: 5.5 }, | |
{ count: 200, speed: 10.0, size: 8.0 }, | |
]; | |
const rand = (min, max) => min + Math.random() * (max - min); | |
// Handle resize | |
function resize() { | |
width = window.innerWidth; | |
height = window.innerHeight; | |
canvas.width = width; | |
canvas.height = height; | |
// Create background with enhanced nebula effect | |
bgCanvas = document.createElement("canvas"); | |
bgCanvas.width = width; | |
bgCanvas.height = height; | |
bgCtx = bgCanvas.getContext("2d"); | |
// Base gradient - pure black background | |
const bgGrad = bgCtx.createRadialGradient( | |
width / 2, height / 2, 0, | |
width / 2, height / 2, width * 0.8 | |
); | |
bgGrad.addColorStop(0, "#000000"); | |
bgGrad.addColorStop(0.5, "#000000"); | |
bgGrad.addColorStop(1, "#000000"); | |
bgCtx.fillStyle = bgGrad; | |
bgCtx.fillRect(0, 0, width, height); | |
// Enhanced nebula effect with multiple layers - more vibrant | |
const nebulaCount = Math.floor(width * height / 30000); | |
bgCtx.globalCompositeOperation = "screen"; | |
for (let i = 0; i < nebulaCount; i++) { | |
const x = rand(0, width); | |
const y = rand(0, height); | |
const radius = rand(100, 400); | |
const hue = rand(220, 280); | |
const alpha = rand(0.03, 0.08); | |
const grd = bgCtx.createRadialGradient( | |
x, y, 0, | |
x, y, radius | |
); | |
grd.addColorStop(0, `hsla(${hue}, 100%, 100%, ${alpha})`); | |
grd.addColorStop(0.3, `hsla(${hue + 20}, 100%, 80%, ${alpha * 0.8})`); | |
grd.addColorStop(1, `hsla(${hue - 20}, 100%, 50%, 0)`); | |
bgCtx.beginPath(); | |
bgCtx.fillStyle = grd; | |
bgCtx.arc(x, y, radius, 0, Math.PI * 2); | |
bgCtx.fill(); | |
} | |
// Add subtle star clusters - brighter | |
bgCtx.globalCompositeOperation = "lighter"; | |
for (let i = 0; i < nebulaCount / 3; i++) { | |
const x = rand(0, width); | |
const y = rand(0, height); | |
const clusterSize = rand(20, 60); | |
const clusterGrad = bgCtx.createRadialGradient( | |
x, y, 0, | |
x, y, clusterSize | |
); | |
clusterGrad.addColorStop(0, `rgba(255, 255, 255, 0.2)`); | |
clusterGrad.addColorStop(1, `rgba(200, 220, 255, 0)`); | |
bgCtx.beginPath(); | |
bgCtx.fillStyle = clusterGrad; | |
bgCtx.arc(x, y, clusterSize, 0, Math.PI * 2); | |
bgCtx.fill(); | |
} | |
bgCtx.globalCompositeOperation = "source-over"; | |
} | |
// Pre-render enhanced star sprites with glow | |
function createStarSprites() { | |
starSprites = layerConfigs.map(cfg => { | |
const r = cfg.size; | |
const sz = Math.ceil(r * 8); | |
const off = document.createElement("canvas"); | |
off.width = off.height = sz; | |
const octx = off.getContext("2d"); | |
// Core glow with gradient - brighter for black background | |
const gradient = octx.createRadialGradient( | |
sz/2, sz/2, 0, | |
sz/2, sz/2, r * 3 | |
); | |
gradient.addColorStop(0, "#ffffff"); | |
gradient.addColorStop(0.3, "#a0c0ff"); | |
gradient.addColorStop(1, "rgba(50, 100, 255, 0)"); | |
octx.fillStyle = gradient; | |
octx.beginPath(); | |
octx.arc(sz/2, sz/2, r * 3, 0, Math.PI * 2); | |
octx.fill(); | |
// Bright core with corona - enhanced brightness | |
octx.shadowColor = "#ffffff"; | |
octx.shadowBlur = r * 3; | |
octx.globalCompositeOperation = "lighter"; | |
octx.fillStyle = "#ffffff"; | |
octx.beginPath(); | |
octx.arc(sz/2, sz/2, r * 0.8, 0, Math.PI * 2); | |
octx.fill(); | |
// Secondary corona - enhanced brightness | |
octx.shadowColor = "#a0c0ff"; | |
octx.shadowBlur = r * 5; | |
octx.fillStyle = "rgba(180, 220, 255, 0.9)"; | |
octx.beginPath(); | |
octx.arc(sz/2, sz/2, r * 0.6, 0, Math.PI * 2); | |
octx.fill(); | |
return off; | |
}); | |
} | |
// Initialize star layers with enhanced twinkle parameters | |
function initStars() { | |
layers = layerConfigs.map((cfg, idx) => { | |
const stars = []; | |
for (let i = 0; i < cfg.count; i++) { | |
stars.push({ | |
x: rand(-width/2, width/2), | |
y: rand(-height/2, height/2), | |
z: rand(1, width), | |
twPhase: rand(0, Math.PI * 2), | |
twFreq: rand(0.01, 0.05), | |
twRange: rand(0.3, 0.9), | |
sprIndex: idx, | |
hue: rand(180, 220), // Blue-ish stars | |
saturation: rand(80, 100), | |
brightness: rand(90, 100), | |
pulsePhase: rand(0, Math.PI * 2), | |
pulseSpeed: rand(0.02, 0.08), | |
originalSize: cfg.size | |
}); | |
} | |
return { ...cfg, stars }; | |
}); | |
} | |
// Spawn comets with enhanced effects | |
function maybeComet() { | |
if (Math.random() < 0.003) { | |
const angle = Math.PI * 0.25 + rand(-0.5, 0.5); | |
comets.push({ | |
x: rand(-100, width + 100), | |
y: rand(-100, height * 0.4), | |
len: 0, | |
maxLen: rand(100, 250), | |
angle: angle, | |
speed: rand(12, 20), | |
segments: [], | |
alpha: 1.0, | |
hue: rand(180, 220), | |
size: rand(2, 5) | |
}); | |
} | |
} | |
// Create particle explosion | |
function createParticles(x, y, count, color) { | |
for (let i = 0; i < count; i++) { | |
particles.push({ | |
x: x, | |
y: y, | |
vx: rand(-3, 3), | |
vy: rand(-3, 3), | |
life: 1.0, | |
decay: rand(0.01, 0.03), | |
size: rand(1, 3), | |
color: color || `hsl(${rand(180, 220)}, 100%, ${rand(80, 100)}%)` | |
}); | |
} | |
} | |
// Click creates enhanced warp pulse with particles | |
canvas.addEventListener("click", e => { | |
// Create main pulses | |
for (let i = 0; i < 5; i++) { | |
pulses.push({ | |
x: e.clientX, | |
y: e.clientY, | |
r: i * 15, | |
maxR: 200 + i * 60, | |
thickness: 20 - i * 2, | |
alpha: 0.9 - i * 0.15, | |
speed: 10 - i * 1.5, | |
hue: rand(180, 220) | |
}); | |
} | |
// Create particle explosion | |
createParticles(e.clientX, e.clientY, 50, `hsl(${rand(180, 220)}, 100%, 90%)`); | |
}); | |
// Mouse move for star attraction | |
canvas.addEventListener("mousemove", e => { | |
mouse.x = e.clientX; | |
mouse.y = e.clientY; | |
mouse.active = true; | |
}); | |
canvas.addEventListener("mouseleave", () => { | |
mouse.active = false; | |
}); | |
// Update positions and effects | |
function update() { | |
const now = Date.now(); | |
// Update stars with mouse attraction | |
layers.forEach(layer => { | |
layer.stars.forEach(s => { | |
s.z -= layer.speed; | |
// Mouse attraction effect | |
if (mouse.active) { | |
const dx = (mouse.x - width/2) - s.x; | |
const dy = (mouse.y - height/2) - s.y; | |
const dist = Math.sqrt(dx*dx + dy*dy); | |
if (dist < 300) { | |
const force = (300 - dist) / 300 * 0.5; | |
s.x += dx * force * (s.z / width); | |
s.y += dy * force * (s.z / width); | |
} | |
} | |
if (s.z <= 0) { | |
s.z = width; | |
s.x = rand(-width/2, width/2); | |
s.y = rand(-height/2, height/2); | |
} | |
// Update twinkle and pulsing | |
s.twPhase += s.twFreq; | |
s.pulsePhase += s.pulseSpeed; | |
}); | |
}); | |
// Update comets | |
comets = comets.filter(c => c.alpha > 0.05); | |
comets.forEach(c => { | |
c.len += c.speed; | |
const headX = c.x + Math.cos(c.angle) * c.len; | |
const headY = c.y + Math.sin(c.angle) * c.len; | |
// Add new segment | |
c.segments.push({ | |
x: headX, | |
y: headY, | |
alpha: 1.0, | |
size: c.size * (0.5 + 0.5 * Math.random()) | |
}); | |
// Fade old segments | |
c.segments.forEach(seg => seg.alpha -= 0.015); | |
// Remove old segments | |
if (c.segments.length > 30) c.segments.shift(); | |
// Fade entire comet | |
c.alpha -= 0.002; | |
// Occasionally create particles | |
if (Math.random() < 0.3) { | |
createParticles(headX, headY, 1, `hsl(${c.hue}, 100%, 80%)`); | |
} | |
}); | |
// Update pulses | |
pulses = pulses.filter(p => p.r < p.maxR); | |
pulses.forEach(p => { | |
p.r += p.speed; | |
p.alpha -= 0.006; | |
}); | |
// Update particles | |
particles = particles.filter(p => p.life > 0); | |
particles.forEach(p => { | |
p.x += p.vx; | |
p.y += p.vy; | |
p.vy += 0.05; // Gravity | |
p.life -= p.decay; | |
p.vx *= 0.98; // Friction | |
p.vy *= 0.98; | |
}); | |
maybeComet(); | |
} | |
// Draw the enhanced scene | |
function draw() { | |
// Background with subtle movement | |
ctx.clearRect(0, 0, width, height); | |
ctx.drawImage(bgCanvas, 0, 0); | |
// Subtle motion blur effect | |
ctx.fillStyle = "rgba(0, 0, 0, 0.05)"; | |
ctx.fillRect(0, 0, width, height); | |
// Draw particles | |
particles.forEach(p => { | |
ctx.globalAlpha = p.life; | |
ctx.fillStyle = p.color; | |
ctx.beginPath(); | |
ctx.arc(p.x, p.y, Math.max(0.1, p.size * p.life), 0, Math.PI * 2); | |
ctx.fill(); | |
}); | |
// Stars with enhanced effects | |
layers.forEach((layer, li) => { | |
const sprite = starSprites[li]; | |
ctx.globalCompositeOperation = "lighter"; | |
layer.stars.forEach(s => { | |
const k = (width/2) / s.z; | |
const x = s.x * k + width/2; | |
const y = s.y * k + height/2; | |
if (x < -100 || x > width+100 || y < -100 || y > height+100) return; | |
// Calculate twinkle with pulsing | |
const twinkle = 0.5 + 0.5 * Math.sin(s.twPhase); | |
const pulse = 0.8 + 0.2 * Math.sin(s.pulsePhase); | |
const alpha = (s.twRange + (1 - s.twRange) * twinkle) * pulse; | |
const ds = Math.max(0.1, (1 - s.z/width) * s.originalSize * 2 * pulse); | |
ctx.globalAlpha = alpha; | |
const drawSize = Math.max(0.1, ds * 2); | |
ctx.drawImage(sprite, x - ds, y - ds, drawSize, drawSize); | |
}); | |
}); | |
ctx.globalCompositeOperation = "source-over"; | |
ctx.globalAlpha = 1; | |
// Comets with enhanced particle trails | |
comets.forEach(c => { | |
ctx.lineCap = "round"; | |
for (let i = 1; i < c.segments.length; i++) { | |
const seg1 = c.segments[i-1]; | |
const seg2 = c.segments[i]; | |
const gradient = ctx.createLinearGradient( | |
seg1.x, seg1.y, seg2.x, seg2.y | |
); | |
const segAlpha = Math.min(seg1.alpha, seg2.alpha) * c.alpha; | |
gradient.addColorStop(0, `hsla(${c.hue}, 100%, 100%, ${segAlpha})`); | |
gradient.addColorStop(0.5, `hsla(${c.hue - 20}, 100%, 80%, ${segAlpha * 0.8})`); | |
gradient.addColorStop(1, `hsla(${c.hue - 40}, 100%, 60%, ${segAlpha * 0.3})`); | |
ctx.strokeStyle = gradient; | |
ctx.lineWidth = Math.max(0.1, seg2.size * (i/c.segments.length) * c.alpha); | |
ctx.beginPath(); | |
ctx.moveTo(seg1.x, seg1.y); | |
ctx.lineTo(seg2.x, seg2.y); | |
ctx.stroke(); | |
} | |
}); | |
// Warp pulses with enhanced rings | |
pulses.forEach(p => { | |
ctx.beginPath(); | |
ctx.arc(p.x, p.y, Math.max(0.1, p.r), 0, Math.PI * 2); | |
const gradient = ctx.createRadialGradient( | |
p.x, p.y, Math.max(0.1, p.r - p.thickness/2), | |
p.x, p.y, Math.max(0.1, p.r + p.thickness/2) | |
); | |
gradient.addColorStop(0, `hsla(${p.hue}, 100%, 100%, ${p.alpha})`); | |
gradient.addColorStop(0.3, `hsla(${p.hue - 10}, 100%, 90%, ${p.alpha * 0.9})`); | |
gradient.addColorStop(0.7, `hsla(${p.hue - 20}, 100%, 70%, ${p.alpha * 0.5})`); | |
gradient.addColorStop(1, `hsla(${p.hue - 30}, 100%, 50%, 0)`); | |
ctx.strokeStyle = gradient; | |
ctx.lineWidth = Math.max(0.1, p.thickness); | |
ctx.stroke(); | |
// Add inner glow | |
if (p.alpha > 0.3) { | |
const innerRadius = Math.max(0.1, p.r * 0.3); | |
ctx.beginPath(); | |
ctx.arc(p.x, p.y, innerRadius, 0, Math.PI * 2); | |
const innerGradient = ctx.createRadialGradient( | |
p.x, p.y, 0, | |
p.x, p.y, innerRadius | |
); | |
innerGradient.addColorStop(0, `hsla(${p.hue + 20}, 100%, 100%, ${p.alpha * 0.7})`); | |
innerGradient.addColorStop(1, `hsla(${p.hue}, 100%, 80%, 0)`); | |
ctx.fillStyle = innerGradient; | |
ctx.fill(); | |
} | |
}); | |
} | |
// Animation loop | |
function loop() { | |
update(); | |
draw(); | |
requestAnimationFrame(loop); | |
} | |
// Initialize | |
function init() { | |
resize(); | |
createStarSprites(); | |
initStars(); | |
window.addEventListener("resize", () => { | |
resize(); | |
initStars(); | |
}); | |
// Add some initial particles for visual interest | |
for (let i = 0; i < 100; i++) { | |
setTimeout(() => { | |
createParticles(rand(0, width), rand(0, height), 3, `hsl(${rand(180, 220)}, 100%, ${rand(70, 90)}%)`); | |
}, i * 100); | |
} | |
loop(); | |
} | |
init(); | |
})(); |