Worms
Gemma 4 31b · basic

Reviews & generation details
Reviews
No human review yet. Unreviewed does not mean unsuccessful.
Generation context
Imported historical artifact. Its exact original instructions and lineage are not known.
Generation prompt · exact text unavailable
See the current prompt for reference. It may differ from the instructions used to generate this artifact.
Inspect original source 26,299 bytes · SHA-256 47863a283476
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WormWars: Chaos Artillery</title>
<style>
:root {
--primary: #ffcc00;
--accent: #ff4400;
--bg: #1a1a2e;
--panel: rgba(0, 0, 0, 0.7);
}
body, html {
margin: 0; padding: 0; overflow: hidden;
background: var(--bg);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
user-select: none;
}
#game-container {
position: relative;
width: 100vw;
height: 100vh;
}
canvas { display: block; }
#hud {
position: absolute;
top: 0; left: 0; width: 100%; height: 100%;
pointer-events: none;
display: none;
}
.ui-panel {
position: absolute;
background: var(--panel);
color: white;
padding: 15px;
border-radius: 12px;
border: 2px solid var(--primary);
pointer-events: auto;
backdrop-filter: blur(5px);
box-shadow: 0 0 20px rgba(0,0,0,0.5);
}
#turn-info {
top: 20px; left: 50%;
transform: translateX(-50%);
text-align: center;
min-width: 200px;
}
#timer-bar-container {
width: 100%; height: 10px;
background: #333;
margin-top: 10px;
border-radius: 5px;
overflow: hidden;
}
#timer-bar {
width: 100%; height: 100%;
background: var(--primary);
transition: width 0.1s linear;
}
#weapon-dock {
bottom: 20px; left: 50%;
transform: translateX(-50%);
display: flex;
gap: 10px;
}
.weapon-btn {
width: 60px; height: 60px;
background: #444;
border: 2px solid #888;
color: white;
border-radius: 8px;
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 10px;
transition: all 0.2s;
}
.weapon-btn.active {
border-color: var(--primary);
background: #666;
transform: scale(1.1);
box-shadow: 0 0 15px var(--primary);
}
.weapon-btn:hover { background: #555; }
#wind-indicator {
top: 20px; right: 20px;
width: 120px; text-align: center;
}
#wind-arrow {
font-size: 24px;
display: inline-block;
transition: transform 0.3s;
}
#menu {
position: absolute;
top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.85);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 100;
color: white;
}
.menu-card {
background: #2c3e50;
padding: 30px;
border-radius: 20px;
border: 4px solid var(--primary);
text-align: center;
max-width: 500px;
}
.btn-main {
padding: 15px 40px;
font-size: 24px;
background: var(--primary);
border: none;
border-radius: 50px;
cursor: pointer;
font-weight: bold;
color: #000;
margin-top: 20px;
transition: transform 0.2s;
}
.btn-main:hover { transform: scale(1.1); }
#combat-log {
position: absolute;
bottom: 100px;
left: 20px;
max-height: 200px;
overflow-y: hidden;
color: #aaa;
font-size: 14px;
pointer-events: none;
}
.floating-text {
position: absolute;
color: #ff4444;
font-weight: bold;
pointer-events: none;
animation: floatUp 1s ease-out forwards;
}
@keyframes floatUp {
0% { transform: translateY(0); opacity: 1; }
100% { transform: translateY(-50px); opacity: 0; }
}
</style>
</head>
<body>
<div id="game-container">
<canvas id="gameCanvas"></canvas>
<div id="hud">
<div id="turn-info" class="ui-panel">
<div id="current-turn-name" style="font-size: 20px; font-weight: bold;">Team 1's Turn</div>
<div id="timer-bar-container"><div id="timer-bar"></div></div>
</div>
<div id="wind-indicator" class="ui-panel">
<div style="font-size: 12px;">WIND</div>
<div id="wind-arrow">➔</div>
<div id="wind-speed">0</div>
</div>
<div id="weapon-dock" class="ui-panel">
<!-- Weapons injected via JS -->
</div>
<div id="combat-log"></div>
</div>
<div id="menu">
<div class="menu-card">
<h1 style="font-size: 48px; margin-top: 0; color: var(--primary);">WormWars</h1>
<p>Destructible Terrain • Strategic Artillery • Absolute Chaos</p>
<div style="margin: 20px 0; display: grid; grid-template-columns: 1fr 1fr; gap: 10px; text-align: left;">
<label>Teams (2-4): <input type="number" id="team-count" value="2" min="2" max="4" style="width: 40px;"></label>
<label>Worms/Team: <input type="number" id="worm-count" value="3" min="1" max="4" style="width: 40px;"></label>
<label>CPU Teams: <input type="number" id="cpu-count" value="1" min="0" max="4" style="width: 40px;"></label>
<label>Map:
<select id="map-type">
<option value="islands">Double Islands</option>
<option value="single">Single Island</option>
<option value="cave">The Cave</option>
</select>
</label>
</div>
<button class="btn-main" onclick="startGame()">START BATTLE</button>
</div>
</div>
</div>
<script>
/**
* WormWars Engine
* Built as a single-file high-performance canvas application.
* Features: Destructible pixel-terrain, custom physics, and hot-seat AI.
*/
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
const terrainCanvas = document.createElement('canvas');
const tctx = terrainCanvas.getContext('2d', { willReadFrequently: true });
// Game Configuration
const CONFIG = {
gravity: 0.25,
waterLevel: 0,
friction: 0.98,
turnTime: 30,
colors: ['#ff4444', '#44ff44', '#4444ff', '#ffff44'],
weaponry: [
{ id: 'bazooka', name: 'Bazooka', power: 15, radius: 40, type: 'indirect', color: '#aaa', icon: '🚀' },
{ id: 'cluster', name: 'Cluster', power: 10, radius: 30, type: 'indirect', color: '#ff0', icon: '💥' },
{ id: 'sniper', name: 'Sniper', power: 30, radius: 10, type: 'direct', color: '#fff', icon: '🎯' },
{ id: 'airstrike', name: 'Airstrike', power: 20, radius: 80, type: 'map', color: '#f0f', icon: '⚡' },
{ id: 'mine', name: 'Cluster Mine', power: 5, radius: 20, type: 'indirect', color: '#0f0', icon: '💣' },
{ id: 'teleport', name: 'Jump Pad', power: 0, radius: 0, type: 'utility', color: '#0ff', icon: '🌀' }
]
};
let state = {
phase: 'MENU',
teams: [],
activeTeamIdx: 0,
activeWormIdx: 0,
wind: 0,
timer: 0,
camera: { x: 0, y: 0, zoom: 1 },
input: { mouseX: 0, mouseY: 0, isMouseDown: false, power: 0 },
selectedWeapon: 0,
particles: [],
projectiles: [],
debris: [],
shake: 0,
freeze: 0
};
// Audio System (Procedural)
const audio = {
ctx: new (window.AudioContext || window.webkitAudioContext)(),
play(freq, type, duration, vol = 0.1) {
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.type = type;
osc.frequency.setValueAtTime(freq, this.ctx.currentTime);
gain.gain.setValueAtTime(vol, this.ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + duration);
osc.connect(gain);
gain.connect(this.ctx.destination);
osc.start();
osc.stop(this.ctx.currentTime + duration);
},
sound(effect) {
switch(effect) {
case 'shoot': this.play(150, 'square', 0.2); break;
case 'explode': this.play(60, 'sawtooth', 0.5, 0.3); break;
case 'hit': this.play(300, 'sine', 0.1); break;
case 'click': this.play(800, 'sine', 0.05); break;
case 'water': this.play(200, 'sine', 0.3); break;
}
}
};
class Particle {
constructor(x, y, color) {
this.x = x; this.y = y;
this.vx = (Math.random() - 0.5) * 10;
this.vy = (Math.random() - 0.5) * 10;
this.life = 1.0;
this.color = color;
this.size = Math.random() * 4 + 2;
}
update() {
this.x += this.vx; this.y += this.vy;
this.vy += 0.1;
this.life -= 0.02;
}
draw() {
ctx.globalAlpha = this.life;
ctx.fillStyle = this.color;
ctx.fillRect(this.x - state.camera.x, this.y - state.camera.y, this.size, this.size);
ctx.globalAlpha = 1.0;
}
}
class Debris {
constructor(x, y, w, h) {
this.x = x; this.y = y; this.w = w; this.h = h;
this.vx = (Math.random() - 0.5) * 4;
this.vy = (Math.random() - 0.5) * 4;
this.rot = 0;
this.vr = (Math.random() - 0.5) * 0.2;
}
update() {
this.vx *= 0.99;
this.vy += CONFIG.gravity;
this.x += this.vx;
this.y += this.vy;
this.rot += this.vr;
}
draw() {
ctx.save();
ctx.translate(this.x - state.camera.x, this.y - state.camera.y);
ctx.rotate(this.rot);
ctx.fillStyle = '#654321';
ctx.fillRect(-this.w/2, -this.h/2, this.w, this.h);
ctx.restore();
}
}
class Projectile {
constructor(x, y, vx, vy, weapon) {
this.x = x; this.y = y; this.vx = vx; this.vy = vy;
this.weapon = weapon;
this.radius = 4;
this.dead = false;
}
update() {
this.vx += state.wind * 0.01;
this.vy += CONFIG.gravity;
this.x += this.vx;
this.y += this.vy;
if (this.y > terrainCanvas.height || this.x < 0 || this.x > terrainCanvas.width) {
this.explode();
}
if (checkCollision(this.x, this.y)) {
this.explode();
}
}
explode() {
this.dead = true;
audio.sound('explode');
state.shake = this.weapon.radius / 2;
state.freeze = 5;
const radius = this.weapon.radius;
digTerrain(this.x, this.y, radius);
// Damage Worms
state.teams.forEach(team => {
team.worms.forEach(worm => {
const dist = Math.hypot(worm.x - this.x, worm.y - this.y);
if (dist < radius * 1.5) {
const dmg = Math.floor(this.weapon.power * (1 - dist/(radius*1.5)));
worm.hp -= dmg;
createFloatingText(worm.x, worm.y, `-${dmg}`);
}
});
});
// Debris creation
for(let i=0; i<5; i++) {
state.debris.push(new Debris(this.x, this.y, Math.random()*10+5, Math.random()*10+5));
}
// Particles
for(let i=0; i<20; i++) {
state.particles.push(new Particle(this.x, this.y, '#ffaa00'));
}
// Special Weapon Effects
if (this.weapon.id === 'cluster') {
for(let i=0; i<5; i++) {
state.projectiles.push(new Projectile(this.x, this.y, (Math.random()-0.5)*10, -Math.random()*10, {id:'small', power:5, radius:15}));
}
}
}
draw() {
ctx.fillStyle = this.weapon.color;
ctx.beginPath();
ctx.arc(this.x - state.camera.x, this.y - state.camera.y, this.radius, 0, Math.PI*2);
ctx.fill();
}
}
class Worm {
constructor(x, y, teamIdx, wormIdx) {
this.x = x; this.y = y;
this.teamIdx = teamIdx;
this.wormIdx = wormIdx;
this.hp = 100;
this.vx = 0;
this.vy = 0;
this.radius = 6;
this.isGrounded = false;
this.moveTimer = 0;
}
update() {
this.vx *= CONFIG.friction;
this.vy += CONFIG.gravity;
let nextX = this.x + this.vx;
let nextY = this.y + this.vy;
// Collision logic
if (checkCollision(nextX, nextY)) {
if (this.vy > 5) {
this.hp -= Math.floor(this.vy);
createFloatingText(this.x, this.y, 'Ouch!', '#fff');
}
this.vy = 0;
this.vx *= 0.5;
this.isGrounded = true;
} else {
this.isGrounded = false;
}
this.x += this.vx;
this.y += this.vy;
// Map bounds
if (this.x < 0) this.x = 0;
if (this.x > terrainCanvas.width) this.x = terrainCanvas.width;
// Water drowning
if (this.y > CONFIG.waterLevel + 100) {
this.hp -= 0.1;
if (Math.random() < 0.05) audio.sound('water');
}
}
draw() {
ctx.fillStyle = CONFIG.colors[this.teamIdx];
ctx.beginPath();
ctx.arc(this.x - state.camera.x, this.y - state.camera.y, this.radius, 0, Math.PI*2);
ctx.fill();
// HP Bar
ctx.fillStyle = '#000';
ctx.fillRect(this.x - state.camera.x - 10, this.y - state.camera.y - 15, 20, 4);
ctx.fillStyle = '#0f0';
ctx.fillRect(this.x - state.camera.x - 10, this.y - state.camera.y - 15, (this.hp/100)*20, 4);
}
}
// Terrain Systems
function generateTerrain(type) {
tctx.fillStyle = '#87CEEB'; // Sky
tctx.fillRect(0, 0, terrainCanvas.width, terrainCanvas.height);
tctx.fillStyle = '#4a7a3a'; // Grass
tctx.beginPath();
if (type === 'single') {
tctx.moveTo(0, terrainCanvas.height);
tctx.lineTo(0, terrainCanvas.height);
for(let x=0; x<=terrainCanvas.width; x+=20) {
let y = terrainCanvas.height * 0.7 + Math.sin(x*0.01)*50 + Math.random()*20;
if (x > 200 && x < terrainCanvas.width-200) y -= 100;
tctx.lineTo(x, y);
}
tctx.lineTo(terrainCanvas.width, terrainCanvas.height);
} else if (type === 'islands') {
tctx.moveTo(0, terrainCanvas.height);
for(let x=0; x<=terrainCanvas.width; x+=20) {
let y = terrainCanvas.height * 0.8 + Math.sin(x*0.01)*40;
if ((x > 200 && x < 600) || (x > 1400 && x < 1800)) y -= 150;
tctx.lineTo(x, y);
}
tctx.lineTo(terrainCanvas.width, terrainCanvas.height);
} else { // cave
tctx.moveTo(0, terrainCanvas.height);
tctx.lineTo(0, 200);
for(let x=0; x<=terrainCanvas.width; x+=40) {
tctx.lineTo(x, 200 + Math.random()*100);
}
tctx.lineTo(terrainCanvas.width, 200);
tctx.lineTo(terrainCanvas.width, terrainCanvas.height);
}
tctx.fill();
// Water
CONFIG.waterLevel = terrainCanvas.height * 0.85;
tctx.fillStyle = 'rgba(0, 0, 255, 0.4)';
tctx.fillRect(0, CONFIG.waterLevel, terrainCanvas.width, terrainCanvas.height - CONFIG.waterLevel);
}
function digTerrain(cx, cy, radius) {
tctx.globalCompositeOperation = 'destination-out';
tctx.beginPath();
tctx.arc(cx, cy, radius, 0, Math.PI*2);
tctx.fill();
tctx.globalCompositeOperation = 'source-over';
}
function checkCollision(x, y) {
if (x < 0 || x >= terrainCanvas.width || y < 0 || y >= terrainCanvas.height) return false;
const pixel = tctx.getImageData(x, y, 1, 1).data;
return pixel[3] > 128; // Alpha channel check
}
// Core Game Loop
function update() {
if (state.freeze > 0) {
state.freeze--;
return;
}
// Wind Shift
if (Math.random() < 0.005) {
state.wind = (Math.random() - 0.5) * 4;
document.getElementById('wind-arrow').style.transform = `rotate(${state.wind > 0 ? 0 : 180}deg)`;
document.getElementById('wind-speed').innerText = Math.abs(state.wind).toFixed(1);
}
// Projectiles
state.projectiles.forEach((p, i) => {
p.update();
if (p.dead) state.projectiles.splice(i, 1);
});
// Worms
state.teams.forEach(team => {
team.worms.forEach(worm => {
worm.update();
if (worm.hp <= 0) worm.hp = 0;
});
});
// Debris & Particles
state.debris = state.debris.filter(d => {
d.update();
return d.y < terrainCanvas.height;
});
state.particles.forEach((p, i) => {
p.update();
if (p.life <= 0) state.particles.splice(i, 1);
});
// Turn Management
if (state.phase === 'ACTION') {
state.timer -= 1/60;
const timerBar = document.getElementById('timer-bar');
const percent = (state.timer / CONFIG.turnTime) * 100;
timerBar.style.width = percent + '%';
if (percent < 30) timerBar.style.background = 'red';
else timerBar.style.background = 'var(--primary)';
if (state.timer <= 0) nextTurn();
}
// Camera Smoothing
const activeWorm = state.teams[state.activeTeamIdx].worms[state.activeWormIdx];
if (activeWorm) {
const targetX = activeWorm.x - canvas.width/2;
const targetY = activeWorm.y - canvas.height/2;
state.camera.x += (targetX - state.camera.x) * 0.1;
state.camera.y += (targetY - state.camera.y) * 0.1;
}
if (state.shake > 0) {
state.camera.x += (Math.random()-0.5) * state.shake;
state.camera.y += (Math.random()-0.5) * state.shake;
state.shake *= 0.9;
}
}
function render() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw Terrain
ctx.drawImage(terrainCanvas, -state.camera.x, -state.camera.y);
// Draw Entities
state.debris.forEach(d => d.draw());
state.projectiles.forEach(p => p.draw());
state.particles.forEach(p => p.draw());
state.teams.forEach(team => {
team.worms.forEach(worm => worm.draw());
});
// Aim Line
if (state.phase === 'ACTION' && state.input.isMouseDown) {
const worm = state.teams[state.activeTeamIdx].worms[state.activeWormIdx];
ctx.beginPath();
ctx.setLineDash([5, 5]);
ctx.strokeStyle = 'rgba(255,255,255,0.5)';
let lx = worm.x, ly = worm.y;
let lvx = state.input.vx, lvy = state.input.vy;
for(let i=0; i<60; i++) {
ctx.lineTo(lx - state.camera.x, ly - state.camera.y);
lvx += state.wind * 0.01;
lvy += CONFIG.gravity;
lx += lvx; ly += lvy;
if (checkCollision(lx, ly)) break;
}
ctx.stroke();
ctx.setLineDash([]);
}
}
function loop() {
update();
render();
requestAnimationFrame(loop);
}
// Game Logic
function startGame() {
audio.sound('click');
const tCount = parseInt(document.getElementById('team-count').value);
const wCount = parseInt(document.getElementById('worm-count').value);
const cpuCount = parseInt(document.getElementById('cpu-count').value);
const mapType = document.getElementById('map-type').value;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
terrainCanvas.width = 2000;
terrainCanvas.height = 1000;
generateTerrain(mapType);
state.teams = [];
for(let i=0; i<tCount; i++) {
const team = {
name: `Team ${i+1}`,
isCPU: i < cpuCount,
worms: []
};
for(let j=0; j<wCount; j++) {
// Find safe spawn
let sx, sy;
do {
sx = 100 + Math.random() * 1800;
sy = 200 + Math.random() * 600;
} while (checkCollision(sx, sy));
team.worms.push(new Worm(sx, sy, i, j));
}
state.teams.push(team);
}
setupWeaponDock();
document.getElementById('menu').style.display = 'none';
document.getElementById('hud').style.display = 'block';
state.phase = 'ACTION';
startTurn();
loop();
}
function setupWeaponDock() {
const dock = document.getElementById('weapon-dock');
dock.innerHTML = '';
CONFIG.weaponry.forEach((wp, i) => {
const btn = document.createElement('div');
btn.className = `weapon-btn ${i === 0 ? 'active' : ''}`;
btn.innerHTML = `<span>${wp.icon}</span><span>${wp.name}</span>`;
btn.onclick = () => {
audio.sound('click');
state.selectedWeapon = i;
document.querySelectorAll('.weapon-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
};
dock.appendChild(btn);
});
}
function startTurn() {
const team = state.teams[state.activeTeamIdx];
const worm = team.worms[state.activeWormIdx];
document.getElementById('current-turn-name').innerText = `${team.name}'s Turn`;
document.getElementById('current-turn-name').style.color = CONFIG.colors[state.activeTeamIdx];
state.timer = CONFIG.turnTime;
if (team.isCPU) {
state.phase = 'CPU';
setTimeout(runAI, 1000);
} else {
state.phase = 'ACTION';
}
}
function nextTurn() {
state.activeWormIdx++;
if (state.activeWormIdx >= state.teams[state.activeTeamIdx].worms.length) {
state.activeWormIdx = 0;
state.activeTeamIdx = (state.activeTeamIdx + 1) % state.teams.length;
}
// Remove dead worms from turn rotation
if (state.teams[state.activeTeamIdx].worms[state.activeWormIdx].hp <= 0) {
nextTurn();
} else {
startTurn();
}
}
function runAI() {
const team = state.teams[state.activeTeamIdx];
const worm = team.worms[state.activeWormIdx];
// Simple Risk-Assessment AI
// Find nearest enemy
let target = null;
let minDist = Infinity;
state.teams.forEach(t => {
if (t === team) return;
t.worms.forEach(w => {
if (w.hp > 0) {
const d = Math.hypot(w.x - worm.x, w.y - worm.y);
if (d < minDist) { minDist = d; target = w; }
}
});
});
if (target) {
const dx = target.x - worm.x;
const dy = target.y - worm.y;
const angle = Math.atan2(dy, dx);
const dist = Math.hypot(dx, dy);
// Calculate projectile force (approximate parabolic)
const force = Math.sqrt(dist) * 0.8;
const vx = Math.cos(angle) * force;
const vy = Math.sin(angle) * force - 5;
state.input.vx = vx;
state.input.vy = vy;
setTimeout(() => {
fireWeapon();
setTimeout(nextTurn, 2000);
}, 1000);
} else {
nextTurn();
}
}
function fireWeapon() {
const worm = state.teams[state.activeTeamIdx].worms[state.activeWormIdx];
const weapon = CONFIG.weaponry[state.selectedWeapon];
audio.sound('shoot');
if (weapon.type === 'utility' && weapon.id === 'teleport') {
worm.vy = -10;
worm.vx += (Math.random()-0.5)*5;
} else if (weapon.type === 'map' && weapon.id === 'airstrike') {
const tx = state.input.tx || worm.x + 200;
const ty = state.input.ty || worm.y;
for(let i=0; i<3; i++) {
setTimeout(() => {
state.projectiles.push(new Projectile(tx + (i-1)*100, 0, 0, 15, weapon));
}, i * 200);
}
} else {
state.projectiles.push(new Projectile(worm.x, worm.y, state.input.vx, state.input.vy, weapon));
}
}
function createFloatingText(x, y, text, color = '#ff4444') {
const div = document.createElement('div');
div.className = 'floating-text';
div.innerText = text;
div.style.left = (x - state.camera.x) + 'px'; // Simplified, will flicker on cam move
div.style.top = (y - state.camera.y) + 'px';
div.style.color = color;
document.body.appendChild(div);
setTimeout(() => div.remove(), 1000);
}
// Controls
window.onmousedown = (e) => {
if (state.phase !== 'ACTION') return;
state.input.isMouseDown = true;
state.input.startX = e.clientX;
state.input.startY = e.clientY;
audio.sound('click');
};
window.onmousemove = (e) => {
if (!state.input.isMouseDown) return;
const dx = e.clientX - state.input.startX;
const dy = e.clientY - state.input.startY;
state.input.vx = -dx * 0.1;
state.input.vy = -dy * 0.1;
};
window.onmouseup = (e) => {
if (!state.input.isMouseDown) return;
state.input.isMouseDown = false;
// Set airstrike target if that's the weapon
if (CONFIG.weaponry[state.selectedWeapon].id === 'airstrike') {
state.input.tx = e.clientX + state.camera.x;
state.input.ty = e.clientY + state.camera.y;
}
fireWeapon();
};
window.onkeydown = (e) => {
if (state.phase !== 'ACTION') return;
const worm = state.teams[state.activeTeamIdx].worms[state.activeWormIdx];
if (e.key === 'a' || e.key === 'ArrowLeft') worm.vx = -2;
if (e.key === 'd' || e.key === 'ArrowRight') worm.vx = 2;
if (e.key === 'w' || e.key === 'ArrowUp') {
if (worm.isGrounded) {
worm.vy = -6;
audio.sound('click');
}
}
};
window.onresize = () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
};
</script>
</body>
</html>
<!-- agent-meta {"model":"gemma-4-31b","provider":"cerebras","persona":"basic","sessionId":"77f96d2b-952c-45e2-8ed9-c5a26b5995b3","tokensIn":559,"tokensOut":8580,"tokensTotal":9139,"turns":1,"toolCalls":0,"failedToolCalls":0,"timestamp":"2026-07-14T21:56:53.578Z"} -->