chaos-cannon-crt / index.html
LukasBe's picture
Add 3 files
d0feeee verified
<!DOCTYPE html>
<html>
<head>
<title>Chaos Cannon - CRT Edition</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
body {
background: #000;
overflow: hidden;
font-family: 'Courier New', monospace;
}
#crt-screen {
position: relative;
width: 800px;
height: 600px;
margin: 20px auto;
background: #111;
border-radius: 10px;
box-shadow: 0 0 20px rgba(0, 255, 0, 0.3),
inset 0 0 50px rgba(0, 255, 0, 0.1);
overflow: hidden;
padding: 10px;
}
#crt-effect {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(rgba(0, 255, 0, 0.1) 1px, transparent 1px);
background-size: 100% 2px;
pointer-events: none;
z-index: 10;
}
#crt-curve {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 50% 50% 50% 50% / 10% 10% 10% 10%;
box-shadow: inset 0 0 20px rgba(0, 255, 0, 0.2);
pointer-events: none;
z-index: 5;
}
#crt-glow {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(circle at center, rgba(0, 255, 0, 0.05) 0%, transparent 70%);
pointer-events: none;
z-index: 1;
}
#crt-noise {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABOSURBVGhD7c4xAQAgDAAx9I9h6Q8O3kQk5+0BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgBx5hBQHXQ1JQAAAAAElFTkSuQmCC');
opacity: 0.03;
pointer-events: none;
z-index: 15;
}
#game {
display: block;
width: 100%;
height: 100%;
background: #000;
border-radius: 5px;
}
.crt-reflection {
position: absolute;
top: 50%;
left: 50%;
width: 100%;
height: 100%;
background: linear-gradient(135deg, rgba(0,255,0,0.1) 0%, transparent 50%);
transform: translate(-50%, -50%);
pointer-events: none;
z-index: 20;
}
.crt-label {
position: absolute;
bottom: 10px;
right: 20px;
color: #0f0;
font-size: 12px;
text-shadow: 0 0 5px #0f0;
z-index: 30;
}
.crt-power {
position: absolute;
bottom: 10px;
left: 20px;
color: #0f0;
font-size: 12px;
text-shadow: 0 0 5px #0f0;
z-index: 30;
}
.crt-power::before {
content: "";
display: inline-block;
width: 8px;
height: 8px;
background: #0f0;
border-radius: 50%;
margin-right: 5px;
box-shadow: 0 0 5px #0f0;
animation: power-pulse 1s infinite alternate;
}
@keyframes power-pulse {
0% { opacity: 0.5; }
100% { opacity: 1; }
}
@keyframes flicker {
0% { opacity: 0.9; }
5% { opacity: 0.8; }
10% { opacity: 0.95; }
15% { opacity: 0.85; }
20% { opacity: 1; }
25% { opacity: 0.9; }
30% { opacity: 0.98; }
35% { opacity: 0.87; }
40% { opacity: 0.94; }
45% { opacity: 0.91; }
50% { opacity: 0.99; }
100% { opacity: 1; }
}
.flicker {
animation: flicker 0.1s infinite;
}
</style>
</head>
<body class="bg-black flex items-center justify-center h-screen">
<div id="crt-screen" class="relative">
<div id="crt-glow"></div>
<div id="crt-curve"></div>
<canvas id="game" width="800" height="600"></canvas>
<div id="crt-effect"></div>
<div id="crt-noise"></div>
<div class="crt-reflection"></div>
<div class="crt-power">POWER</div>
<div class="crt-label">CHAOS CANNON v1.0</div>
</div>
<script>
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
// Add vintage color palette
const colors = {
background: '#0a0a0a',
turretBase: '#00ff00',
turretBarrel: '#00cc00',
projectile: '#ff6600',
enemy: '#00ff00',
text: '#00ff00',
particle1: '#ff3300',
particle2: '#ff9900',
particle3: '#ffff00'
};
class Game {
constructor() {
this.state = 'title';
this.score = 0;
this.shake = 0;
this.turret = new Turret();
this.projectiles = [];
this.enemies = [];
this.particles = [];
this.explosions = [];
this.flickerTimer = 0;
canvas.addEventListener('click', (e) => {
if(this.state === 'title') {
this.start();
} else if(this.state === 'playing') {
this.turret.shoot(this);
}
});
canvas.addEventListener('mousemove', (e) => {
this.turret.angle = Math.atan2(
e.clientY - canvas.offsetTop - this.turret.y,
e.clientX - canvas.offsetLeft - this.turret.x
);
});
}
start() {
this.state = 'playing';
this.score = 0;
this.spawnEnemies();
}
spawnEnemies() {
setInterval(() => {
this.enemies.push(new Enemy(
Math.random() * canvas.width,
-50,
Math.random() * 2 + 1
));
}, 1500);
}
update() {
if(this.state !== 'playing') return;
this.flickerTimer++;
if(this.flickerTimer > 10) {
this.flickerTimer = 0;
document.getElementById('crt-effect').classList.toggle('flicker');
}
// Update all entities
this.turret.update();
this.projectiles.forEach(p => p.update(this));
this.enemies.forEach(e => e.update(this));
this.particles.forEach(p => p.update());
this.explosions.forEach(e => e.update(this));
// Check collisions
this.projectiles.forEach(projectile => {
this.enemies.forEach((enemy, i) => {
if(Math.hypot(
projectile.x - enemy.x,
projectile.y - enemy.y
) < 20) {
this.explosions.push(new Explosion(enemy.x, enemy.y));
this.enemies.splice(i, 1);
this.score += 100;
this.shake = 10;
this.playExplosionSound();
}
});
});
// Cleanup
this.projectiles = this.projectiles.filter(p => !p.dead);
this.particles = this.particles.filter(p => !p.dead);
this.explosions = this.explosions.filter(e => !e.dead);
}
draw() {
ctx.save();
if(this.shake > 0) {
ctx.translate(
Math.random() * this.shake - this.shake/2,
Math.random() * this.shake - this.shake/2
);
this.shake *= 0.9;
}
// Draw scanlines effect
ctx.fillStyle = 'rgba(0, 20, 0, 0.3)';
for(let y = 0; y < canvas.height; y += 2) {
ctx.fillRect(0, y, canvas.width, 1);
}
// Draw vignette
const gradient = ctx.createRadialGradient(
canvas.width/2, canvas.height/2, 0,
canvas.width/2, canvas.height/2, Math.max(canvas.width, canvas.height)/2
);
gradient.addColorStop(0, 'rgba(0, 0, 0, 0)');
gradient.addColorStop(0.7, 'rgba(0, 10, 0, 0.5)');
gradient.addColorStop(1, 'rgba(0, 20, 0, 0.8)');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw all entities
this.turret.draw(ctx);
this.projectiles.forEach(p => p.draw(ctx));
this.enemies.forEach(e => e.draw(ctx));
this.particles.forEach(p => p.draw(ctx));
this.explosions.forEach(e => e.draw(ctx));
// UI
ctx.fillStyle = colors.text;
ctx.font = '20px "Courier New", monospace';
ctx.fillText(`SCORE: ${this.score}`, 20, 30);
if(this.state === 'title') {
ctx.fillStyle = 'rgba(0,0,0,0.8)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Title text with glow effect
ctx.fillStyle = colors.text;
ctx.font = 'bold 48px "Courier New", monospace';
const title = 'CHAOS CANNON';
const titleWidth = ctx.measureText(title).width;
ctx.fillText(title, canvas.width/2 - titleWidth/2, canvas.height/2);
// Glow effect
for(let i = 0; i < 3; i++) {
ctx.strokeStyle = `rgba(0, 255, 0, ${0.3 - i*0.1})`;
ctx.lineWidth = 5 - i*2;
ctx.strokeText(title, canvas.width/2 - titleWidth/2, canvas.height/2);
}
ctx.font = '24px "Courier New", monospace';
ctx.fillText('CLICK TO START', canvas.width/2 - 100, canvas.height/2 + 50);
}
ctx.restore();
}
playExplosionSound() {
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.type = 'square';
oscillator.frequency.value = 100 + Math.random() * 100;
gainNode.gain.setValueAtTime(0.2, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.3);
oscillator.start();
oscillator.stop(audioContext.currentTime + 0.3);
}
}
class Turret {
constructor() {
this.x = canvas.width/2;
this.y = canvas.height - 50;
this.angle = 0;
this.cooldown = 0;
}
update() {
if(this.cooldown > 0) this.cooldown--;
}
shoot(game) {
if(this.cooldown <= 0) {
game.projectiles.push(new Projectile(
this.x + Math.cos(this.angle) * 40,
this.y + Math.sin(this.angle) * 40,
Math.cos(this.angle) * 15,
Math.sin(this.angle) * 15
));
this.cooldown = 10;
}
}
draw(ctx) {
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.angle);
// Barrel
ctx.fillStyle = colors.turretBarrel;
ctx.fillRect(0, -5, 40, 10);
// Add barrel glow
const barrelGradient = ctx.createLinearGradient(0, -5, 40, 10);
barrelGradient.addColorStop(0, 'rgba(0, 255, 0, 0.8)');
barrelGradient.addColorStop(1, 'rgba(0, 200, 0, 0.8)');
ctx.fillStyle = barrelGradient;
ctx.fillRect(0, -5, 40, 10);
// Base
ctx.beginPath();
ctx.arc(0, 0, 30, 0, Math.PI * 2);
// Add base glow
const baseGradient = ctx.createRadialGradient(0, 0, 10, 0, 0, 30);
baseGradient.addColorStop(0, 'rgba(0, 255, 0, 0.8)');
baseGradient.addColorStop(1, 'rgba(0, 100, 0, 0.8)');
ctx.fillStyle = baseGradient;
ctx.fill();
ctx.restore();
}
}
class Projectile {
constructor(x, y, vx, vy) {
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
this.dead = false;
}
update(game) {
this.x += this.vx;
this.y += this.vy;
this.vy += 0.2;
if(this.y > canvas.height + 50) this.dead = true;
// Trail particles
const color = Math.random() > 0.5 ? colors.particle1 :
Math.random() > 0.5 ? colors.particle2 : colors.particle3;
game.particles.push(new Particle(
this.x, this.y,
this.vx * -0.2 + Math.random() * 2 - 1,
this.vy * -0.2 + Math.random() * 2 - 1,
color, 20
));
}
draw(ctx) {
// Add glow effect
const glowGradient = ctx.createRadialGradient(
this.x, this.y, 0,
this.x, this.y, 8
);
glowGradient.addColorStop(0, 'rgba(255, 100, 0, 0.8)');
glowGradient.addColorStop(1, 'rgba(255, 50, 0, 0)');
ctx.fillStyle = glowGradient;
ctx.beginPath();
ctx.arc(this.x, this.y, 8, 0, Math.PI * 2);
ctx.fill();
// Projectile core
ctx.fillStyle = colors.projectile;
ctx.beginPath();
ctx.arc(this.x, this.y, 3, 0, Math.PI * 2);
ctx.fill();
}
}
class Enemy {
constructor(x, y, speed) {
this.x = x;
this.y = y;
this.speed = speed;
this.flicker = 0;
}
update(game) {
this.y += this.speed;
this.flicker = (this.flicker + 1) % 10;
if(this.y > canvas.height - 50) {
game.state = 'gameover';
}
}
draw(ctx) {
ctx.save();
ctx.translate(this.x, this.y);
// Flicker effect
if(this.flicker < 2) {
ctx.fillStyle = 'rgba(0, 255, 0, 0.7)';
} else {
ctx.fillStyle = colors.enemy;
}
ctx.beginPath();
ctx.moveTo(-15, 0);
ctx.lineTo(15, 0);
ctx.lineTo(0, 30);
ctx.fill();
// Add glow
const glowGradient = ctx.createLinearGradient(-15, 0, 15, 30);
glowGradient.addColorStop(0, 'rgba(0, 255, 0, 0.3)');
glowGradient.addColorStop(1, 'rgba(0, 150, 0, 0.3)');
ctx.fillStyle = glowGradient;
ctx.beginPath();
ctx.moveTo(-15, 0);
ctx.lineTo(15, 0);
ctx.lineTo(0, 30);
ctx.fill();
ctx.restore();
}
}
class Explosion {
constructor(x, y) {
this.x = x;
this.y = y;
this.particles = [];
this.dead = false;
for(let i = 0; i < 50; i++) {
const hue = Math.random() * 30 + 20;
this.particles.push(new Particle(
x, y,
Math.random() * 10 - 5,
Math.random() * 10 - 5,
`hsl(${hue}, 100%, 50%)`,
40
));
}
}
update(game) {
this.particles.forEach(p => p.update());
this.dead = this.particles.every(p => p.dead);
}
draw(ctx) {
this.particles.forEach(p => p.draw(ctx));
// Add explosion glow
if(!this.dead) {
const glowGradient = ctx.createRadialGradient(
this.x, this.y, 0,
this.x, this.y, 50
);
glowGradient.addColorStop(0, 'rgba(255, 100, 0, 0.5)');
glowGradient.addColorStop(1, 'rgba(255, 50, 0, 0)');
ctx.fillStyle = glowGradient;
ctx.beginPath();
ctx.arc(this.x, this.y, 50, 0, Math.PI * 2);
ctx.fill();
}
}
}
class Particle {
constructor(x, y, vx, vy, color, life) {
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
this.color = color;
this.life = life;
this.dead = false;
this.size = Math.random() * 3 + 1;
}
update() {
this.x += this.vx;
this.y += this.vy;
this.vy += 0.1;
this.life -= 1;
this.dead = this.life <= 0;
}
draw(ctx) {
// Add glow
const glowGradient = ctx.createRadialGradient(
this.x, this.y, 0,
this.x, this.y, this.size * 2
);
glowGradient.addColorStop(0, this.color);
glowGradient.addColorStop(1, 'rgba(255, 255, 0, 0)');
ctx.fillStyle = glowGradient;
ctx.beginPath();
ctx.arc(this.x, this.y, this.size * 2, 0, Math.PI * 2);
ctx.fill();
// Particle core
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fill();
}
}
// Game loop
const game = new Game();
function frame() {
game.update();
game.draw();
requestAnimationFrame(frame);
}
frame();
// Add CRT power on effect
setTimeout(() => {
document.getElementById('crt-screen').style.boxShadow =
'0 0 20px rgba(0, 255, 0, 0.5), inset 0 0 50px rgba(0, 255, 0, 0.2)';
}, 500);
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=LukasBe/chaos-cannon-crt" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>