oneshotlm
Compare all models on this prompt →

Aquarium breach · kimi-k2.5

Create a large glass aquarium whose side panel develops a visible crack and then bursts. The simulation must include: - Water escaping through the opening with flow strength based on water depth and decreasing as the tank drains - A curved water jet affected by gravity - A spreading puddle that collides with the room boundaries - Fish, rocks, plants, and a floating toy reacting differently according to density, buoyancy, drag, and current - Objects transitioning correctly from underwater motion to airborne motion and then to floor collisions - Fish attempting to swim against the current before being swept through the breach - Glass fragments with angular velocity, collisions, and water resistance - A visible waterline that lowers continuously rather than disappearing all at once Let the user drag the crack vertically before triggering the failure. A lower crack should initially produce a stronger jet than a higher crack. Give me 1 html file

⚠ no runnable output

This one-shot run produced no runnable artifact.

agent produced no index.html

The transcript below shows what the model did — read it to see why the one shot fell short.

transcript · 2 messages
user

Create a large glass aquarium whose side panel develops a visible crack and then bursts. The simulation must include: - Water escaping through the opening with flow strength based on water depth and decreasing as the tank drains - A curved water jet affected by gravity - A spreading puddle that collides with the room boundaries - Fish, rocks, plants, and a floating toy reacting differently according to density, buoyancy, drag, and current - Objects transitioning correctly from underwater motion to airborne motion and then to floor collisions - Fish attempting to swim against the current before being swept through the breach - Glass fragments with angular velocity, collisions, and water resistance - A visible waterline that lowers continuously rather than disappearing all at once Let the user drag the crack vertically before triggering the failure. A lower crack should initially produce a stronger jet than a higher crack. Give me 1 html file

assistant
thinking
The user now wants a glass aquarium cracking simulation, which is a relatively complex physics simulation project. I need to first understand the user's specific requirements.  

The user wants a side panel of a large glass aquarium to develop a visible crack and then shatter. The simulation needs to include multiple physical effects: water flow, gravity, object interactions, etc. It seems like a very detailed physics simulation project.  

Let me analyze the key requirements: the crack needs to be draggable, and the jet strength should vary depending on the crack's position. It also needs to simulate water flow, object motion, glass fragments, and more. This project will involve Canvas rendering, physics engine, collision detection, and other aspects.  

I'm thinking about how to structure the code. It might require several main classes: one to manage the water body, one to handle objects (fish, stones, plants, toys), one for glass fragments, and one for water droplets. Each class would need its own physics update and rendering logic.  

For the water body simulation, I need to consider fluid dynamics. When the crack appears, water should flow out, and the jet strength should be related to the water depth. Torricelli's theorem could be used to calculate the flow rate: v = sqrt(2gh). As the water level drops, the jet speed should decrease accordingly.  

In terms of object physics, different objects should react differently based on their density. Stones should sink, wooden toys should float, and fish should swim. The fish should also have AI behavior, attempting to swim against the current. This requires simulating buoyancy, drag, and collision responses.  

The cracking effect is also crucial. The crack should be draggable, and clicking a button should trigger the shattering. When the glass shatters, fragments should be generated, with physical properties like rotation and velocity.  

For interface design, the top could have a title and status information, while the bottom could house control buttons. The main section would be the Canvas display area. A simple UI to show pressure and status might be necessary.  

I'm considering the performance issue of the physics engine. Too many objects might cause lag, so optimization is needed. Spatial partitioning or limiting the number of objects could help.  

The water droplet system is also important. When water jets out, it should generate water droplet particles that splash upon hitting the ground and form water stains. Water stains should have a spreading effect.  

For AI behavior, the fish should detect the crack's position and attempt to swim in the opposite direction. However, if the water flow is too strong, they should still be swept away. Simple steering behaviors could be implemented.  

Now, I feel the plan is clear, and I can start coding. I'll begin by setting up the HTML structure, then implement each physics class step by step, and finally integrate all the features.

```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>Aquarium Burst Simulation</title> <style> :root { --bg: #0b1020; --panel: #0f1630; --text: #cfefff; --accent: #ff2a2a; --accent2: #00d4ff; --glass: rgba(200,230,255,0.08); --glass-border: rgba(200,230,255,0.25); } html, body { height: 100%; } body { margin: 0; background: radial-gradient(1200px 800px at 10% 10%, #0b1836 0%, #080b18 60%, #05070f 100%); color: var(--text); font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji"; display: flex; align-items: center; justify-content: center; overflow: hidden; } #app { position: relative; width: min(1200px, 96vw); height: min(800px, 90vh); background: radial-gradient(900px 500px at 70% 10%, rgba(0,212,255,0.06) 0%, transparent 60%), linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,0.35) 100%); border: 1px solid rgba(255,255,255,0.06); border-radius: 12px; box-shadow: 0 20px 60px rgba(0,0,0,0.6), inset 0 0 0 1px rgba(255,255,255,0.04); overflow: hidden; display: grid; grid-template-rows: auto 1fr auto; } header { padding: 12px 16px; display: flex; align-items: center; justify-content: space-between; gap: 12px; background: linear-gradient(180deg, rgba(0,0,0,0.25), rgba(0,0,0,0.05)); border-bottom: 1px solid rgba(255,255,255,0.06); } h1 { font-size: 18px; margin: 0; letter-spacing: 0.3px; } .hint { opacity: 0.85; font-size: 13px; } .controls { display: flex; gap: 8px; align-items: center; } button { background: linear-gradient(180deg, rgba(0,212,255,0.12), rgba(0,212,255,0.05)); border: 1px solid rgba(0,212,255,0.35); color: #b6f0ff; padding: 8px 12px; border-radius: 8px; cursor: pointer; font-weight: 600; } button.primary { background: linear-gradient(180deg, rgba(255,42,42,0.2), rgba(255,42,42,0.08)); border-color: rgba(255,100,100,0.5); color: #ffd6d6; } button:disabled { opacity: 0.5; cursor: not-allowed; } canvas { width: 100%; height: 100%; display: block; } .overlay { position: absolute; left: 12px; top: 44px; pointer-events: none; } .pill { background: rgba(0,0,0,0.35); border: 1px solid rgba(255,255,255,0.08); padding: 6px 10px; border-radius: 999px; font-size: 12px; color: #cfefff; } </style> </head> <body> <div id="app"> <header> <div> <h1>Aquarium Burst</h1> <div class="hint">Drag the crack on the glass to position it, then Burst.</div> </div> <div class="controls"> <button id="btnReset">Reset</button> <button id="btnBurst" class="primary" disabled>Burst</button> </div> </header> <div style="position: relative;"> <canvas id="c"></canvas> <div class="overlay"> <div class="pill">Pressure: <span id="pressure">0%</span> · Status: <span id="status">Intact</span></div> </div> </div> </div> <script> (() => { const canvas = document.getElementById('c'); const ctx = canvas.getContext('2d', { alpha: false }); const pressureEl = document.getElementById('pressure'); const statusEl = document.getElementById('status'); // Configuration const TANK_W = 800; const TANK_H = 400; const WALL_THICK = 24; const FLOOR_Y = 600; // floor level inside canvas const WATER_BASE = 360; // initial water height inside tank const G = 0.4; // gravity const AIR_DRAG = 0.999; const WATER_DRAG = 0.99; const DT = 1; // State let tankX = 0, tankY = 0; let crackY = 0; let intact = true; let waterLevel = WATER_BASE; let t = 0; // Entities const fishes = []; const rocks = []; const plants = []; const toy = { x: 0, y: 0, r: 14, vx: 0, vy: 0, angle: 0, vAngle: 0, inWater: true, density: 0.55 }; const shards = []; const droplets = []; const splatters = []; const bubbles = []; function resize() { const rect = canvas.parentElement.getBoundingClientRect(); canvas.width = Math.max(1, Math.floor(rect.width)); canvas.height = Math.max(1, Math.floor(rect.height)); tankX = (canvas.width - TANK_W) / 2; tankY = (canvas.height - TANK_H) / 2 + 40; resetSim(); } function resetSim() { intact = true; waterLevel = WATER_BASE; crackY = tankY + TANK_H * 0.55; fishes.length = 0; rocks.length = 0; plants.length = 0; shards.length = 0; droplets.length = 0; splatters.length = 0; bubbles.length = 0; // spawn fish for (let i = 0; i < 10; i++) { fishes.push({ x: tankX + 60 + Math.random() * (TANK_W - 120), y: tankY + 40 + Math.random() * (WATER_BASE - 60), vx: (Math.random() - 0.5) * 2, vy: (Math.random() - 0.5) * 2, r: 8 + Math.random() * 6, angle: Math.random() * Math.PI * 2, inWater: true, swimTimer: Math.random() * 100, }); } // rocks for (let i = 0; i < 6; i++) { rocks.push({ x: tankX + 40 + Math.random() * (TANK_W - 80), y: tankY + TANK_H - 20 - Math.random() * 40, r: 16 + Math.random() * 18, vx: 0, vy: 0, angle: Math.random() * Math.PI * 2, vAngle: 0, inWater: true, density: 2.6, }); } // plants for (let i = 0; i < 5; i++) { plants.push({ x: tankX + 60 + Math.random() * (TANK_W - 120), y: tankY + TANK_H - 8, h: 80 + Math.random() * 60, sways: [], }); const p = plants[plants.length - 1]; const segs = 8; for (let s = 0; s <= segs; s++) p.sways.push({ offset: 0, speed: 0.003 + Math.random() * 0.005, phase: Math.random() * Math.PI * 2 }); } // toy toy.x = tankX + TANK_W * 0.8; toy.y = tankY + 50; toy.vx = 0; toy.vy = 0; toy.angle = 0; toy.vAngle = 0; toy.inWater = true; updateUI(); } function updateUI() { const depth = Math.max(0, waterLevel - (crackY - tankY)); const percent = Math.min(100, Math.max(0, Math.round((depth / WATER_BASE) * 100))); pressureEl.textContent = `${percent}%`; statusEl.textContent = intact ? 'Intact' : 'Breached'; document.getElementById('btnBurst').disabled = !intact; } // Input handling let dragging = false; function getPos(e) { const rect = canvas.getBoundingClientRect(); const x = (e.clientX ?? e.touches?.[0]?.clientX ?? 0) - rect.left; const y = (e.clientY ?? e.touches?.[0]?.clientY ?? 0) - rect.top; return { x, y }; } function hitCrack(x, y) { const cx = tankX + TANK_W + WALL_THICK / 2; const cy = crackY; const dx = x - cx; const dy = y - cy; return dx * dx + dy * dy < 24 * 24; } canvas.addEventListener('mousedown', (e) => { const p = getPos(e); if (intact && hitCrack(p.x, p.y)) { dragging = true; } }); canvas.addEventListener('mousemove', (e) => { const p = getPos(e); if (intact) { canvas.style.cursor = hitCrack(p.x, p.y) ? 'grab' : 'default'; } else canvas.style.cursor = 'default'; if (dragging) { crackY = Math.max(tankY + 20, Math.min(tankY + TANK_H - 20, p.y)); updateUI(); } }); window.addEventListener('mouseup', () => { dragging = false; }); // Touch support canvas.addEventListener('touchstart', (e) => { const p = getPos(e); if (intact && hitCrack(p.x, p.y)) { dragging = true; } }, { passive: false }); canvas.addEventListener('touchmove', (e) => { if (dragging) { e.preventDefault(); const p = getPos(e); crackY = Math.max(tankY + 20, Math.min(tankY + TANK_H - 20, p.y)); updateUI(); } }, { passive: false }); window.addEventListener('touchend', () => { dragging = false; }); document.getElementById('btnBurst').addEventListener('click', () => { if (!intact) return; intact = false; // spawn shards const n = 18; for (let i = 0; i < n; i++) { const angle = -Math.PI / 2 + (Math.random() - 0.5) * 1.2; const speed = 3 + Math.random() * 5; shards.push({ x: tankX + TANK_W, y: crackY, vx: Math.cos(angle) * speed + 1, vy: Math.sin(angle) * speed, angle: Math.random() * Math.PI * 2, vAngle: (Math.random() - 0.5) * 0.4, w: 12 + Math.random() * 14, h: 12 + Math.random() * 18, }); } updateUI(); }); document.getElementById('btnReset').addEventListener('click', () => { resetSim(); }); // Physics helpers function inTankWater(x, y) { if (x < tankX + 2 || x > tankX + TANK_W - 2) return false; if (y > tankY + waterLevel) return false; if (y < tankY + 2) return false; return true; } function inAnyWater(x, y) { if (y > tankY + waterLevel) return false; if (x > tankX + TANK_W) return y > tankY + Math.max(0, waterLevel - (x - (tankX + TANK_W)) * 0.2); if (x < tankX) return y > tankY + Math.max(0, waterLevel - (tankX - x) * 0.2); return y > tankY; } function step() { t++; // Water draining if (!intact) { const crackDepth = Math.max(0, waterLevel - (crackY - tankY)); const flow = Math.max(0, Math.sqrt(2 * G * crackDepth) * 0.5); // Torricelli-ish const drain = flow * 0.9; waterLevel = Math.max(0, waterLevel - drain); // Droplets from crack const dropCount = Math.min(6, Math.floor(flow * 0.6)); for (let i = 0; i < dropCount; i++) { const ang = -0.15 + Math.random() * 0.3; const sp = 6 + Math.random() * 6; droplets.push({ x: tankX + TANK_W, y: crackY, vx: Math.cos(ang) * sp + 1, vy: Math.sin(ang) * sp, life: 200 + Math.floor(Math.random() * 100), r: 2 + Math.random() * 2.5, }); } // Splatter spawn on floor for (let i = 0; i < Math.min(3, Math.floor(flow * 0.35)); i++) { splatters.push({ x: tankX + TANK_W + 10 + Math.random() * 80, y: FLOOR_Y, r: 2 + Math.random() * 4, vx: (Math.random() - 0.2) * 1.5, vy: 0, life: 160 + Math.floor(Math.random() * 120), }); } } // Fish for (const f of fishes) { const inW = inTankWater(f.x, f.y); f.inWater = inW; const drag = inW ? WATER_DRAG : AIR_DRAG; const buoyancy = inW ? -G * 0.9 : 0; const targetAngle = f.vx >= 0 ? 0 : Math.PI; // Swim AI if (f.inWater) { f.swimTimer++; // seek away from crack if close const dx = (tankX + TANK_W) - f.x; const dy = crackY - f.y; const d2 = dx * dx + dy * dy; if (!intact && d2 < 200 * 200 && f.x > tankX + TANK_W - 200) { // panic swim away from crack f.vx -= 0.06; f.vy += (Math.random() - 0.5) * 0.15; } else { // wander if (f.swimTimer % 40 === 0) { f.vx += (Math.random() - 0.5) * 0.4; f.vy += (Math.random() - 0.5) * 0.2; } } // limit speed const speed = Math.hypot(f.vx, f.vy); if (speed > 2.5) { f.vx *= 0.96; f.vy *= 0.96; } // wall repulse if (f.x < tankX + 30) f.vx += 0.04; if (f.x > tankX + TANK_W - 30) f.vx -= 0.04; if (f.y < tankY + 20) f.vy += 0.04; if (f.y > tankY + waterLevel - 20) f.vy -= 0.04; } else { // in air f.vy += G; if (f.y > FLOOR_Y - f.r) { f.y = FLOOR_Y - f.r; f.vy *= -0.3; f.vx *= 0.8; } if (f.x < 0) { f.x = 0; f.vx *= -0.5; } if (f.x > canvas.width) { f.x = canvas.width; f.vx *= -0.5; } } f.vy += buoyancy; f.vx *= drag; f.vy *= drag; f.x += f.vx; f.y += f.vy; const angleDiff = targetAngle - f.angle; f.angle += angleDiff * 0.05; } // Rocks for (const r of rocks) { const inW = inTankWater(r.x, r.y); r.inWater = inW; const drag = inW ? 0.92 : AIR_DRAG; const buoyancy = inW ? -G * 0.2 : 0; // heavy r.vy += G + buoyancy; r.vx *= drag; r.vy *= drag; if (!inW && r.y > FLOOR_Y - r.r) { r.y = FLOOR_Y - r.r; r.vy *= -0.25; r.vx *= 0.7; } // tank floor collision if (r.y > tankY + TANK_H - r.r) { r.y = tankY + TANK_H - r.r; r.vy *= -0.2; } // side walls inside tank if (r.x < tankX + r.r) { r.x = tankX + r.r; r.vx *= -0.3; } if (r.x > tankX + TANK_W - r.r) { r.x = tankX + TANK_W - r.r; r.vx *= -0.3; } r.x += r.vx; r.y += r.vy; } // Toy (floating) const tInW = inTankWater(toy.x, toy.y); toy.inWater = tInW; const toyDrag = tInW ? 0.96 : AIR_DRAG; const toyBuoyancy = tInW ? -G * 1.2 : 0; toy.vy += G + toyBuoyancy; toy.vx *= toyDrag; toy.vy *= toyDrag; toy.vAngle *= 0.98; if (!toy.inWater && toy.y > FLOOR_Y - toy.r) { toy.y = FLOOR_Y - toy.r; toy.vy *= -0.25; toy.vx *= 0.7; } if (toy.y > tankY + TANK_H - toy.r) { toy.y = tankY + TANK_H - toy.r; toy.vy *= -0.2; } if (toy.x < tankX + toy.r) { toy.x = tankX + toy.r; toy.vx *= -0.3; } if (toy.x > tankX + TANK_W - toy.r) { toy.x = tankX + TANK_W - toy.r; toy.vx *= -0.3; } toy.x += toy.vx; toy.y += toy.vy; toy.angle += toy.vAngle; // Shards for (const s of shards) { s.vy += G; s.vx *= 0.995; s.vy *= 0.995; s.x += s.vx; s.y += s.vy; s.angle += s.vAngle; if (s.y > FLOOR_Y) { s.y = FLOOR_Y; s.vy *= -0.2; s.vx *= 0.6; } } // Droplets for (let i = droplets.length - 1; i >= 0; i--) { const d = droplets[i]; d.vy += G; d.x += d.vx; d.y += d.vy; d.life--; if (d.y > FLOOR_Y) { droplets.splice(i, 1); continue; } if (d.life <= 0) droplets.splice(i, 1); } // Splatters spreading on floor for (let i = splatters.length - 1; i >= 0; i--) { const s = splatters[i]; s.x += s.vx; s.life--; // spread radius a bit over time s.r = Math.min(12, s.r + 0.08); if (s.x < tankX + TANK_W + 10) { s.vx = Math.abs(s.vx) + 0.01; } if (s.life <= 0) splatters.splice(i, 1); } // Plants sway for (const p of plants) { for (let i = 0; i < p.sways.length; i++) { p.sways[i].offset = Math.sin(t * p.sways[i].speed + p.sways[i].phase) * (2 + i * 0.8); } } // Bubbles occasionally if (t % 20 === 0 && waterLevel > 20) { bubbles.push({ x: tankX + 30 + Math.random() * (TANK_W - 60), y: tankY + TANK_H - 10, r: 2 + Math.random() * 3, vy: -(1 + Math.random()), life: 300, }); } for (let i = bubbles.length - 1; i >= 0; i--) { const b = bubbles[i]; b.y += b.vy; b.life--; if (b.y < tankY + waterLevel - b.r) bubbles.splice(i, 1); } updateUI(); } function draw() { ctx.fillStyle = '#05070f'; ctx.fillRect(0, 0, canvas.width, canvas.height); // Room floor ctx.fillStyle = '#0b1020'; ctx.fillRect(0, FLOOR_Y, canvas.width, canvas.height - FLOOR_Y); // Water body (tank + puddle) ctx.save(); ctx.beginPath(); const surfaceLeft = tankX; const surfaceRight = tankX + TANK_W; const surfaceY = tankY + waterLevel; // tank interior water ctx.moveTo(tankX, tankY + TANK_H); ctx.lineTo(tankX, surfaceY); ctx.lineTo(surfaceRight, surfaceY); ctx.lineTo(surfaceRight, tankY + TANK_H); ctx.closePath(); // spill curve to floor const spillStartX = tankX + TANK_W; const spillStartY = surfaceY; const midX = spillStartX + 120; const midY = tankY + TANK_H + (FLOOR_Y - (tankY + TANK_H)) * 0.35; const endX = spillStartX + 300; const endY = FLOOR_Y; ctx.moveTo(spillStartX, spillStartY); ctx.bezierCurveTo(midX, midY, endX, FLOOR_Y - 20, canvas.width, FLOOR_Y); ctx.lineTo(canvas.width, canvas.height); ctx.lineTo(tankX, canvas.height); ctx.closePath(); ctx.fillStyle = 'rgba(0,212,255,0.12)'; ctx.fill(); ctx.lineWidth = 2; ctx.strokeStyle = 'rgba(0,212,255,0.35)'; ctx.stroke(); // Waterline on the glass (if water inside) if (waterLevel > 2) { ctx.beginPath(); ctx.moveTo(tankX + 4, surfaceY); ctx.lineTo(tankX + TANK_W - 4, surfaceY); ctx.lineWidth = 2; ctx.strokeStyle = 'rgba(200,245,255,0.5)'; ctx.stroke(); } ctx.restore(); // Tank glass back ctx.save(); ctx.strokeStyle = 'rgba(200,230,255,0.18)'; ctx.lineWidth = 2; ctx.strokeRect(tankX, tankY, TANK_W, TANK_H); ctx.restore(); // Plants for (const p of plants) { ctx.save(); ctx.beginPath(); const segs = p.sways.length - 1; const segH = p.h / segs; let cx = p.x, cy = p.y; ctx.moveTo(cx, cy); for (let i = 1; i <= segs; i++) { const sway = p.sways[i].offset; cx = p.x + sway; cy = p.y - i * segH; ctx.lineTo(cx, cy); } ctx.lineCap = 'round'; ctx.lineWidth = 4; ctx.strokeStyle = '#1f8a4d'; ctx.stroke(); ctx.restore(); } // Rocks for (const r of rocks) { ctx.save(); ctx.translate(r.x, r.y); ctx.rotate(r.angle); const g = ctx.createRadialGradient(-r.r/2, -r.r/2, 2, 0, 0, r.r); g.addColorStop(0, '#b6c6d6'); g.addColorStop(1, '#5a6a7a'); ctx.fillStyle = g; ctx.beginPath(); ctx.arc(0, 0, r.r, 0, Math.PI * 2); ctx.fill(); ctx.lineWidth = 1; ctx.strokeStyle = 'rgba(0,0,0,0.3)'; ctx.stroke(); ctx.restore(); } // Toy (floating duck-ish) ctx.save(); ctx.translate(toy.x, toy.y); ctx.rotate(toy.angle); // body ctx.fillStyle = '#ffd166'; ctx.beginPath(); ctx.ellipse(0, 0, toy.r, toy.r * 0.65, 0, 0, Math.PI * 2); ctx.fill(); // head ctx.beginPath(); ctx.arc(toy.r * 0.6, -toy.r * 0.5, toy.r * 0.45, 0, Math.PI * 2); ctx.fill(); // beak ctx.fillStyle = '#ff9f43'; ctx.beginPath(); ctx.moveTo(toy.r * 0.9, -toy.r * 0.5); ctx.lineTo(toy.r * 1.15, -toy.r * 0.45); ctx.lineTo(toy.r * 0.9, -toy.r * 0.35); ctx.fill(); // eye ctx.fillStyle = '#222'; ctx.beginPath(); ctx.arc(toy.r * 0.7, -toy.r * 0.6, 2, 0, Math.PI * 2); ctx.fill(); ctx.restore(); // Fishes for (const f of fishes) { ctx.save(); ctx.translate(f.x, f.y); ctx.rotate(f.angle); const g = ctx.createLinearGradient(-f.r, 0, f.r, 0); g.addColorStop(0, '#ff6b6b'); g.addColorStop(1, '#ff8f8f'); ctx.fillStyle = g; ctx.beginPath(); ctx.ellipse(0, 0, f.r * 1.2, f.r * 0.6, 0, 0, Math.PI * 2); ctx.fill(); // tail ctx.beginPath(); ctx.moveTo(-f.r * 1.1, 0); ctx.lineTo(-f.r * 1.6, -f.r * 0.4); ctx.lineTo(-f.r * 1.6, f.r * 0.4); ctx.fill(); // eye ctx.fillStyle = '#fff'; ctx.beginPath(); ctx.arc(f.r * 0.5, -f.r * 0.15, f.r * 0.18, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#000'; ctx.beginPath(); ctx.arc(f.r * 0.55, -f.r * 0.15, f.r * 0.08, 0, Math.PI * 2); ctx.fill(); ctx.restore(); } // Shards for (const s of shards) { ctx.save(); ctx.translate(s.x, s.y); ctx.rotate(s.angle); ctx.fillStyle = 'rgba(200,230,255,0.35)'; ctx.strokeStyle = 'rgba(255,255,255,0.6)'; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(-s.w/2, -s.h/2); ctx.lineTo(s.w/2, -s.h/3); ctx.lineTo(s.w/3, s.h/2); ctx.lineTo(-s.w/3, s.h/2); ctx.closePath(); ctx.fill(); ctx.stroke(); ctx.restore(); } // Droplets for (const d of droplets) { ctx.save(); ctx.translate(d.x, d.y); ctx.fillStyle = 'rgba(180,235,255,0.9)'; ctx.beginPath(); ctx.arc(0, 0, d.r, 0, Math.PI * 2); ctx.fill(); ctx.restore(); } // Splatters for (const s of splatters) { ctx.save(); ctx.translate(s.x, s.y); ctx.fillStyle = 'rgba(180,235,255,0.55)'; ctx.beginPath(); ctx.ellipse(0, 0, s.r * 1.4, s.r * 0.6, 0, 0, Math.PI * 2); ctx.fill(); ctx.restore(); } // Bubbles for (const b of bubbles) { ctx.save(); ctx.translate(b.x, b.y); ctx.fillStyle = 'rgba(255,255,255,0.25)'; ctx.strokeStyle = 'rgba(255,255,255,0.45)'; ctx.lineWidth = 1; ctx.beginPath(); ctx.arc(0, 0, b.r, 0, Math.PI * 2); ctx.fill(); ctx.stroke(); ctx.restore(); } // Tank walls (overlay) ctx.save(); ctx.strokeStyle = 'rgba(200,230,255,0.25)'; ctx.lineWidth = WALL_THICK; ctx.strokeRect(tankX - WALL_THICK/2, tankY - WALL_THICK/2, TANK_W + WALL_THICK, TANK_H + WALL_THICK); ctx.restore(); // Crack handle const cx = tankX + TANK_W + WALL_THICK / 2; const cy = crackY; ctx.save(); ctx.translate(cx, cy); // crack line ctx.beginPath(); ctx.moveTo(-6, -14); ctx.lineTo(2, -6); ctx.lineTo(-4, 2); ctx.lineTo(4, 10); ctx.lineTo(-2, 20); ctx.lineWidth = 2; ctx.strokeStyle = intact ? 'rgba(255,200,200,0.9)' : 'rgba(255,200,200,0.0)'; ctx.stroke(); // handle ctx.fillStyle = intact ? 'rgba(255,255,255,0.9)' : 'rgba(255,255,255,0.2)'; ctx.beginPath(); ctx.arc(0, 0, 8, 0, Math.PI * 2); ctx.fill(); ctx.restore(); } function loop() { step(); draw(); requestAnimationFrame(loop); } window.addEventListener('resize', resize); resize(); loop(); })(); </script> </body> </html> ```

TAKE IT TO CODE

This run produced no artifact — reuse the prompt with a different model or your own pi-agent run.

RUN

modelkimi-k2.5
vendorMoonshot · Kimi
harnesspi-agent
generated2026-08-02

METRICS

runtime126.2s
tokens in574
tokens out8.1k
cached768
cost / run$0.0170