oneshotlm
Compare all models on this prompt →

Aquarium breach · nex-n2-mini

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 · 8 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
We need respond with code only? User asks create single-page web app in one self-contained file index.html current dir. Need build. Need inspect? We need create file. Need maybe no need read. We need produce complete HTML. Need implement simulation. Need satisfy all. Need maybe use canvas 2D. No external resources. Need include controls? "Let user drag crack vertically before triggering failure. lower crack stronger jet than higher. Give me 1 html file". Need write index.html. Need maybe use CSS JS inline. Need ensure no external. Need maybe run? We can write file. Need perhaps create robust simulation.

We need think through implementation details. Need create one file with canvas full page, UI overlay. Simulation: glass aquarium, crack, bursting, water escaping through opening, flow strength based on water depth and decreasing as drains, curved jet gravity. Puddle collides room boundaries. Fish, rocks, plants, toy react to density, buoyancy, drag, current. Objects transition underwater -> airborne -> floor collisions. Fish swim against current before swept through breach. Glass fragments angular velocity, collisions, water resistance. Visible waterline lowers continuously. User drag crack vertically before triggering failure. Lower crack stronger jet. Need maybe a "trigger failure" button? User can drag crack vertically and click "Trigger Burst". Also maybe crack appears before. Need code.

Need implement physically-ish in 2D. Canvas coordinate. Aquarium tank dimensions. Room floor at y ~ maybe 620. Tank x 100 y 100 w 560 h 420. Water filled to 460? Need crack y draggable along side panel. Opening at x=right side? Side panel crack develops and bursts. Opening at crack location on right wall. Jet exits rightwards, y=crackY, initial vx maybe sqrt(2*g*(waterlineY-crackY))*coefficient (1.0-3.0) plus pressure. Lower crack => bigger head => stronger jet. As water drains waterline lowers. Opening maybe grows over time after burst. Need waterline visible. Need water depth = waterY - crackY? Actually waterline y lower when tank drains. Use waterY. If crackY below waterY? User can drag crack vertically before triggering; if above waterline? Maybe allow but then weak/none? We'll set crackY initially 310? waterline 430; lower crack e.g. 400. Need show label.

Need implement particles:
- water surface: water area in tank clipped to tank rectangle; waterline drawn with sine waves.
- escaping water particles (jets) spawned from breach opening at right wall x tank.x+ tank.w + maybe 4; y = crackY + random. Velocity rightward based on head and downward. Number based on flowStrength. Gravity. Drag.
- puddle particles on floor? Maybe puddle as many droplets/slugs spread horizontally along floor and bounce/wall collisions. Need collide room boundaries: floor y floorY, left/right walls x 0/800. Puddle particles on floor y floorY+? with vx, vy, friction; if hit boundaries bounce/reduce. Also water spreading "puddle" maybe draw as blue translucent blob on floor with particles.
- Objects: fish, rocks, plants, toy. Need objects in tank initially. Each has mass/density, buoyancy, drag, current, type. Update:
- If water inside tank and object y < waterline and x within tank: underwater. Forces: buoyancy upward = density factor? Need model: fish near neutral, toy floats, rock sinks, plant anchored. Drag from current. Current from jet direction near breach? Since water inside tank drains through right crack, current to right near opening. Use current vector field: if object near breach, rightward and downward depending water depth. Underwater:
- Fish: swim against current: fish attempts to swim left with force, but if current strong, swept right. Fish have swimPower, density near neutral, buoyancy small. If swept through breach, x > tank.x+width, transition airborne (out of tank) with velocity, gravity.
- Toy: buoyant floats near waterline, dragged right/down by current, maybe if breached floats out.
- Rock: dense sinks to bottom, dragged by current, may slide/fall out? If breaches, falls to floor.
- Plant: anchored to bottom, sways with current, if crack burst maybe torn? We can animate plant leaning/swaying; maybe if waterline drops or breach, detach fragment? User asked plants reacting differently according to density, buoyancy, drag, current. Plant anchored but current bends it. Maybe plant root stays, blade moves.
- Objects transitioning correctly from underwater to airborne to floor collisions:
- If object x > tank right or waterline below object or object above waterline: set airborne = true; remove underwater drag/buoyancy; gravity; drag; if hits floor, settle, bounce. For toy floats to surface then out; fish out through breach; rock out/falls; plant maybe stem fragment falls.
- Floor collisions: floorY inside room maybe 620. Tank bottom y 520? Room floor maybe 620? Aquarium sits on stand? Need puddle on floor below tank maybe y=560? If tank bottom at 520, floor at 620? Aquarium base maybe. Better tank bottom y=500, floor y=620. Puddle floor at y=620. Objects if fall from tank bottom fall to floor y=620.
- Glass fragments with angular velocity, collisions, water resistance:
- On burst, spawn shards from crack side panel, triangle polygons, velocities, angular velocity. Collide with room boundaries and tank bottom/floor? Glass fragments should collide: with tank edges? We can have floor and walls, maybe tank base. Water resistance: if inside water, velocity damped and angular velocity damped. Draw angular triangles.
- Need visible crack vertical drag before failure:
- UI: canvas with tank side, crack as jagged line at right wall, draggable along side. Before burst, crack draws red/glow line. Cursor changes. Click/drag. Also trigger button. Need maybe "Drag crack up/down, then Trigger Burst". If not triggered, maybe no water leak except maybe "precrack" no flow. But requirement side panel develops visible crack and then bursts. We can draw crack pre-burst and then after burst opening expands, glass fragments.
- Need maybe "reset" button.
- Need "flow strength based on water depth and decreasing as tank drains": compute flow = clamp((waterY - crackY)*0.025,0,1), spawn count and vx. Need waterline lower continuously. As water leaves, waterY -= flow*dt*maybe 12? If crack above waterline, no flow. Use waterY = waterLine.
- Need "curved water jet affected by gravity": water particles with gravity and maybe arc.
- Need "puddle collides with room boundaries": puddle particles with walls.
- Need "objects reacting differently according to density, buoyancy, drag, current": set parameters.
- Need "objects transitioning correctly from underwater motion to airborne motion and then floor collisions": implement.

Need write file. Need maybe test with browser? Can't run JS visual. But can maybe static check. Need ensure no external. Need one file.

Let's design code.

HTML structure:
<!DOCTYPE html><html><head><meta charset...><style>body margin 0 background #0f172a; canvas width:100vw height:100vh display:block; #hud overlay. Controls. Maybe labels. Use button.
</style></head><body>
<canvas id="scene"></canvas>
<div id="hud"> ... <button id=burst>Trigger burst</button> <button id=reset>Reset</button> <div id=readout>...</div></div>
<script>
(() => { ... })();
</script>
</body></html>

Canvas responsive? We can use fixed logical 900x650, scale to fit. CSS canvas 100vw/h. JS set canvas.width = innerWidth * dpr; etc. Use transform? Simpler: use logical W=900 H=650, set canvas style, set canvas.width = W*dpr; ctx.scale(dpr,dpr). But CSS full viewport may stretch. Use W/H constants and center? Use canvas element width/height 900/650. Full page. On resize maybe scale to actual? Need pointer coords convert to logical. Use set canvas dimensions to innerWidth*dpr; ctx.setTransform(dpr*canvas.width/W? Hmm.

Better use logical W=900 H=650 and CSS object-fit. We'll set canvas.width=900; canvas.height=650; CSS width:100vw height:100vh; image-rendering? This stretches to viewport. Pointer conversion: rect, x=(clientX-rect.left)*W/rect.width, y=... Fine.

Need room: W=900 H=650.
Tank = {x:120,y:80,w:560,h:430}; floorY=620; stand maybe {x:80,y:560,w:640,h:30}; room boundaries left=0, right=900, floorY=620, maybe ceiling. Tank bottom=510, top=80. Waterline initial=430. CrackY = 330 maybe lower. Opening x = tank.x+tank.w+3.
Maybe tank glass drawn: rectangle with thick strokes, rounded? Aquarium.
Need visible waterline inside tank: draw water fill from waterline to tank.bottom. But water level drops. If waterline < crackY maybe no leak.

Need draw water:
- Clip to tank interior x+6..x+w-6 y..bottom.
- Draw water fill with gradient from top blue to dark.
- Draw surface line at waterline with sine wave. Since waterline y maybe 430. Draw water fill path from waterline+sin to bottom. Need maybe update waterline continuously.

Need draw tank glass:
- Frame: outer glass rectangle, inner water, highlights. Use ctx.strokeRect, roundRect. Use ctx.globalAlpha.
- Maybe cracks drawn on glass.

Crack:
- crackY. Pre-burst draw jagged vertical line at right inner wall x=tank.x+tank.w-4 from y-crackLen to y+crackLen. Use red/orange glow. If user drags, update. After burst, draw opening: jagged hole polygon/irregular shape along right side around crackY, maybe size = burstGrow. Draw broken glass edge. Opening height grows from 5 to 120 over first 5s. Jet spawn from x = tank.x+tank.w+2, y = crackY + random(-opening/2, opening/2), but if waterline above crackY. Opening size based on burstTime. Lower crack stronger: compute head = max(0, waterY - crackY); flow = 0.05 +? Use head/100. spawnRate = 20 + 300*flow? Need initial strong. Maybe spawn per frame = 8 + 200*flow; dt. Need not too many. Use spawnAccumulator.
- crackY in logical. If crackY below waterline. Lower crack = more head. Need maybe readout: "Crack height: ... Pressure head: ...".
- if user drags crack above waterline before trigger, flow low.

Water particles:
class WaterParticle {x,y,vx,vy,r,life,alpha}
Update: vx *= 0.995; vy += gravity*dt; x += vx*dt; y += vy*dt; if y>floorY or x>W+20 or life>... remove. Draw droplets.
Spawn based on flow: spawnRate = 15 + 260*flow maybe. Each dt, spawnCount = spawnRate*dt. Need if opening >0. Also if waterline > crackY. Opening grows.
Initial vx = 160 + 520*sqrt(flow) maybe. vy = random -30..60 + 80*sqrt(flow). If head lower.
Gravity = 900 px/s^2.
Flow strength maybe = clamp((waterY-crackY)/180,0,1). If lower crack 350, head 80; if lower 420 head10. initial waterline 430, crackY 360 => head70, flow0.39. Strong enough. Maybe initial crackY 340 head90, flow0.5. Need lower stronger.
As tank drains, waterY decreases. Compute drainRate = 4 + 22*flow? But if flow .5 => 15 px/s, waterline lowers over ~10 sec. Good. Also spawn more. Need waterline visible lower.

Puddle:
puddleParticles array {x,y,vx,vy,r,life,ground}. Floor y=620. Spawn on floor at x=tank.x+tank.w+6, y=floorY-2? Actually puddle on floor. Particle y=floorY - r. vx maybe water particle vx; vy maybe. Update:
if airborne (y < floorY-4): vy += 600*dt, y += vy*dt; if y >= floorY-4, y=floorY-4, vy*=-0.15, vx*=0.88. On floor: vy=0, x += vx*dt; vx *= friction (0.96-0.985) per frame? Need dt. if x<20 or x>W-20, x=boundary, vx=-abs(vx)*0.25. If y > floorY, remove.
Need "spreading puddle collides with room boundaries": draw blue translucent puddle maybe particles.
Maybe add "puddle" object with center x, radius, spread speed? Could draw as many particles enough. Particles bounce with walls.

Objects:
Define Object:
type: 'fish'|'rock'|'plant'|'toy'
x,y,vx,vy,r, angle, av, density, buoyancy, drag, mass, swim, alive, underwater, airborne, grounded, anchored, baseY, sway, etc.
Initial:
fish1 at x=240 y=270, r=16, density=1.0, buoyancy=0.02, drag=2.5, swim=80, swimDir=-1, color...
fish2 maybe.
rock at x=220 y=460, r=20, density=2.5, buoyancy=-0.4? drag=0.8, mass=200, color brown, shape.
rock2.
plant at x=390 y=500, r=20, density? anchored, drag=1.2, sway.
toy at x=470 y=240, r=22, density=0.45, buoyancy=0.85, drag=1.4, swim? maybe no.
Maybe fish near breach? To show swept through breach. Place one fish x=455 y=310. But if burst, fish will exit.

Need update objects:
- Determine insideTank = x between tank.x+8 and tank.x+tank.w-8 && y between tank.y+8 and tank.bottom-8.
- underwater = insideTank && y < waterline && !airborne && !grounded maybe.
- current: function currentAt(x,y):
head = max(0, waterline-crackY)
if x > tank.x+tank.w-80 && y > crackY-40 && y < waterline+20:
strength = clamp(head/180,0,1) * (bursting? 1.3:0.15)
vx = strength * (120+260*sqrt(flow)) + maybe if y near crackY downward 80.
else small left-to-right circulation: vx = 8*sin(t*0.5 + y*0.02), vy=0.
- If underwater:
- buoyancy force: upward acceleration = (obj.buoyancy - obj.density)*g? Need sign. Let density relative water. If density<1, buoyancy up. If density>1, downward. Use acc = (1 - obj.density)*180 + obj.buoyancy*100? For toy density0.45 => +117, fish density1.0 =>0, rock density2.5=>-450. But fish needs swim.
- drag: v += -v * obj.drag * dt maybe.
- current: v += current*dt*obj.drag? Actually current pushes: v.x += current.x * obj.drag * dt; v.y += current.y * obj.drag * dt.
- fish swim against current: if current.x > 0, v.x -= fish.swim * dt * (1+current.x/300); also add tail wiggle. If current.x <0, swim maybe +.
- toy: maybe tries to stay near waterline: if y > waterline-10, buoyancy up; if y < waterline-35, downward? Actually toy floats: v.y -= (waterline-y)*? if below surface.
- rock: sinks: if y > bottom-30, grounded.
- plant: anchored root at baseY. If underwater, v.x = current.x*0.4, v.y = current.y*0.4; angle = current.x*0.006 + sin(t)*0.05; no buoyancy. If waterline drops below plant maybe still anchored? Maybe "plants" react to current, not density.
- transitions:
- If underwater && !airborne:
- if obj.type fish and x > tank.x+tank.w-8 and head strong and (maybe x>right-20) after update: triggerAirborne(obj, outVx = v.x+250*sqrt(flow), outVy=v.y+80*sqrt(flow)+100)
- toy if waterline drops below object? If y < waterline? Toy floats up, if breach and x > right-20, out.
- rock if current pushes x > right-20 and y > crackY-20, out.
- plant maybe not exit? If current bends; if root torn when burst, spawn small plant pieces? But requirement objects transitioning; maybe plant can detach? We can allow plant "blade" object to break? But plant anchored. It may not transition, but still reacts.
- If airborne:
- remove water forces; v.y += gravity*dt; v.x *= 1 - drag*dt maybe; x += vx*dt; y += vy*dt; angle += av*dt;
- if y > floorY - r, y=floorY-r, if vy > 100 bounce maybe, else set vy=0, grounded=true, v=0; friction. If x>W-r bounce. if x<0 bounce.
- if airborne and y > floorY + 100 or x>W+100 remove maybe.
- Fish "attempting to swim against current before being swept through breach": Need draw fish facing left maybe with swim stroke. Maybe if current strong, fish angle and tail. Could have readout maybe. We'll implement fish with "swimPower" and "panic" if current strong.
- Plant anchored to floor/stand maybe draw swaying.

Glass fragments:
- shard array {x,y,vx,vy,ax,av,size,angle,points,life,insideWater}
- Spawn at burst maybe 60 shards. Each from crackY. velocities random outward right/down/up, angular velocity. Collisions:
- boundaries: if x<0, x=0, vx=abs(vx)*0.4; if x>W, x=W, vx=-abs(vx)*0.4; if y>floorY, y=floorY, vy=-abs(vy)*0.25, vx*=0.9, av*=0.9; if y<0 bounce.
- tank walls/edges? If intersects tank glass? Maybe not necessary. Need collisions with room boundaries. "collisions" can be with floor/walls. Maybe also tank bottom? We'll handle floor/walls.
- water resistance: if inside water (x between tank.x+4 and tank.x+tank.w-4 and y between waterline and bottom), multiply vx/vy by 0.992 per frame and av by 0.985, maybe slow. Draw fragments.
- Angular velocity and draw rotated triangular shards.

Burst state:
let state='crack'/'bursting'/'draining'?
crackY initial 350.
triggerBurst() sets burst=true, burstTime=0, opening=0, shards spawn. Maybe spawn "glass" and "water" immediate. Opening grows: opening = min(120, burstTime*35 + 20) maybe. If crackY above waterline, opening smaller? But user lower crack. Use opening = min(125, 8 + burstTime*30) if head >0 else 20.
Draw opening:
- ctx.beginPath(); move x=right, y=crackY-opening/2; jagged points along right edge with random deterministic offsets. Need no random each frame? Use deterministic function crackJitter(t). Simpler draw using sin/cos. Use function jaggedY(i, baseY, amp, count). For opening:
points around right wall:
start at right+8, crackY-opening/2
for i 0..10: y = crackY - opening/2 + i/opening*opening; x = right + (i%2?8:-4) + sin(i*1.7+burstTime*10)*6
...
Use path. Fill dark red/black. Stroke white/blue.
- Before burst, draw crack line:
draw jagged vertical line at right wall x=right-5 from crackY-len to crackY+len, stroke red glow, draw "drag me" maybe.

Need maybe use "deterministic random" for cracks/shards? Use Math.random okay. But for repeated opening maybe okay. Need avoid too many random.

Need draw fish/plants/rocks:
- drawObject(o):
- If airborne maybe no water background? If underwater, maybe draw inside water fill. Need draw object after water? Order: background room, stand, water fill, objects, tank glass, crack/opening, particles, fragments, HUD? Actually water fill should be behind objects; draw water before objects. Tank glass stroke after.
- For underwater, draw with slight distortion maybe.
- Fish: body ellipse, tail triangle, fins, eye. If fish.x>right, maybe draw outside too. Need draw current arrows maybe? Could draw faint arrows near breach showing current.
- Rock: irregular polygon.
- Plant: stem curved to current; draw leaves. If current pushes, angle.
- Toy: duck? Floating toy (yellow rubber duck) maybe. Draw circle body, beak, wing. It floats at waterline. If airborne, rotates.

Need draw waterline:
- At waterline, draw line across tank interior, with labels maybe. Use ctx.strokeStyle '#b9f0ff'; lineWidth 3; draw sine wave. Also draw small bubbles? Maybe.

Need update waterline:
- If burst && waterline > crackY maybe waterline -= (4+26*flow)*dt. But flow computed from current waterline. Need if waterline falls below crackY, no flow.
- If not burst maybe waterline constant.
- If waterline below tank bottom? stop.
- If waterline below floor? maybe.

Need "visible waterline lowers continuously rather than disappearing all at once": draw waterline and maybe small label "waterline". Good.

Need "flow strength based on water depth and decreasing as tank drains": readout "Pressure head 72 px; flow 0.40; waterline 430 -> ..." update.

Need "lower crack should initially produce stronger jet": Need if user sets crack lower, readout pressure. Maybe include note. Need maybe if crackY > waterline-10 then flow 0.

Need pointer:
- canvas mousemove: if burst false, check if near right wall and |y-crackY|<30. If yes set dragging=true; else false.
- mousedown if not bursting and near crack: dragging=true.
- mousemove if dragging: crackY=clamp(y, tank.y+15, tank.bottom-15), update readout.
- mouseup release.
- Need maybe pointer events.

Need "trigger failure": Button. Also maybe "Space" triggers? Good.

Need implement resize? Since canvas CSS full screen and logical fixed. We can set canvas width/height to 900/650. CSS maybe. Need dpr? We can set canvas.width=900; height=650; CSS width:100vw. No dpr. Text maybe okay. Could use dpr? If canvas width actual 900, fine. Maybe retina less. But okay.

Need maybe use `roundRect`? CanvasRenderingContext2D.roundRect supported modern. To be safe implement helper roundedRect.

Need code in one file. Need maybe no external resources. Good.

Need maybe include UI instructions. Need maybe overlay not external. Need maybe buttons.

Let's write file. Need be careful syntax. Let's draft JS thoroughly.

Pseudo:

(() => {
const canvas=document.getElementById('scene'); const ctx=canvas.getContext('2d');
const W=900,H=650; canvas.width=W; canvas.height=H;
const floorY=620, tank={x:115,y:80,w:570,h:430,bottom:510,right:685};
let waterY=430;
let crackY=345;
let bursting=false, burstTime=0, opening=0;
let particles=[], puddle=[], shards=[], objects=[], currentArrows=[];
let drag=false, pointer={x:0,y:0};
let last=performance.now();
let spawnWater=0, spawnPuddle=0;
const rand=(a,b)=>a+Math.random()*(b-a);

function clamp...
function sqrt...
function reset() { waterY=430; crackY=345; bursting=false; opening=0; arrays=[]; objects=[...]; }

function currentAt(x,y) {
const flow = flowStrength();
const head=pressureHead();
const nearBreach = x > tank.x+tank.w-110 && y > tank.y+20 && y < waterY+30;
if (!bursting && nearBreach) return {x:8*Math.sin(performance.now()/1000+y*0.03), y:0};
if (nearBreach) {
const s = clamp(head/180,0,1) * (0.25 + burstTime*0.2);
return {x: s*(180+520*Math.sqrt(Math.max(0.01,head/180))), y: s*80*Math.max(0,y-crackY+40)/180};
}
return {x: 0,y:0};
}
function pressureHead(){return Math.max(0,waterY-crackY);}
function flowStrength(){return clamp(pressureHead()/180,0,1);}
function spawnWater(dt) {
if (!bursting) return;
const flow=flowStrength();
if (flow<=0 || waterY<=crackY) return;
const rate=20+360*flow;
spawnWater += rate*dt;
while(spawnWater>1) { spawnWater--; const y=crackY+rand(-opening/2, opening/2); const h=pressureHead(); const headF=clamp(h/180,0,1); const speed=120+520*Math.sqrt(headF); particles.push({x:tank.x+tank.w+4, y: y+rand(-opening*0.12,opening*0.12), vx:speed+rand(-20,20), vy:rand(-40,40)+headF*90, r:rand(1.2,3.2), life:rand(.5,2.5), alpha:rand(.45,.85), angle:rand(0,6.28)});}
}
function updateWater(dt) {
if (!bursting) return;
const flow=flowStrength();
if (flow>0 && waterY>crackY) waterY -= (5+28*flow)*dt;
if (waterY < tank.y+10) waterY=tank.y+10;
// update particles
for backwards...
}
function spawnPuddle(dt) {
if (!bursting) return;
const flow=flowStrength();
if (flow<=0) return;
spawnPuddle += (12+180*flow)*dt;
while spawnPuddle>1:
let p=particles.find? maybe last water particle hitting floor. Use spawn from x=tank.x+tank.w+4 y=floorY+rand(-8,8), vx=60+350*sqrt(flow)+rand(-60,60), vy=0, r=...
puddle.push...
}
function updatePuddle(dt) {
for each p:
if p.y < floorY - p.r: p.vy += 900*dt; p.y += p.vy*dt; if p.y>=floorY-p.r {p.y=floorY-p.r; if p.vy<-?; p.vy=-p.vy*0.18; p.vx*=0.9;}
p.x += p.vx*dt; p.y += p.vy*dt;
if p.x<p.r {p.x=p.r; p.vx=Math.abs(p.vx)*0.25;}
if p.x>W-p.r {p.x=W-p.r; p.vx=-Math.abs(p.vx)*0.25;}
p.vx *= Math.pow(0.985, dt*60); // if dt 16ms
p.life -= dt; remove
}
function updateObjects(dt) {
const t=performance.now()/1000;
for each o:
const inTank = o.x > tank.x+8 && o.x < tank.x+tank.w-8 && o.y > tank.y+8 && o.y < tank.bottom-8;
const under = inTank && o.y < waterY && !o.airborne && !o.grounded;
const c=currentAt(o.x,o.y);
if (o.type==='plant' && !o.airborne && o.root) {
// anchored: compute target x = rootX + current.x*0.5 + sin(t*2+phase)*8; angle = atan2(targetX-rootX, rootY-o.y)? maybe
o.rootX = tank.x+20+...; o.rootY=tank.bottom-10;
o.x = rootX + Math.sin(t*2+phase)*3 + c.x*0.5;
o.y = rootY - 50;
o.angle = clamp(c.x/180 + Math.sin(t*2+phase)*0.2, -0.7,0.7);
o.sway=...
continue;
}
if (under) {
const h=pressureHead();
const flow=clamp(h/180,0,1);
// buoyancy acceleration
const buoy = (1-o.density)*260 + o.buoyancy*180; // toy 0.45 => 143+153=296 up; rock 2.5=> -390-72=-462
o.vy += buoy*dt;
// gravity? buoy includes negative. Maybe add gravity separately? Use gravity=1000*(1-density)? Simpler.
o.vy += 980*(1-o.density)*dt;
// current push
o.vx += c.x * o.drag * dt * (1+flow*2);
o.vy += c.y * o.drag * dt;
// drag
o.vx *= Math.pow(0.985, o.drag*dt*60);
o.vy *= Math.pow(0.985, o.drag*dt*60);
if (o.type==='fish') {
o.swimTimer += dt;
const swim = o.swim * (1 + flow*1.4);
o.vx -= swim * Math.sign(c.x || 1) * dt;
// if near breach and current strong, swim less effective
if (o.x > tank.x+tank.w-70) o.vx += (c.x + flow*280)*dt;
// wiggle
o.angle = Math.sin(t*8 + o.phase)*0.08 - c.x*0.0008;
if (flow>0.15 && o.x > tank.x+tank.w-25) {
o.vx += (260+420*flow)*dt;
o.vy += (70+150*flow)*dt;
}
} else if (o.type==='toy') {
// buoyancy pulls to waterline
o.vy -= clamp((waterY-o.y)*0.008, -120, 120)*dt;
o.vx += (c.x*0.7)*dt;
o.angle += Math.sin(t*5)*0.4*dt;
} else if (o.type==='rock') {
// settle bottom
if (o.y > tank.bottom - 20) {o.y=tank.bottom-20; o.vy=0;}
o.vx += c.x*0.25*dt;
o.angle += c.x*0.0005*dt;
}
// breach transition
if (under && !o.airborne) {
const breachX = tank.x+tank.w-2;
if (o.x > breachX - 18 && flow > 0.12 && c.x > 10 && o.y > crackY-35 && o.y < waterY+10) {
airborneFromBreach(o, c.x + 180 + 400*flow, c.y + 60 + 180*flow);
}
}
} else {
// not underwater: if object grounded at bottom maybe just settle
if (o.grounded) { o.x += c.x*0.05*dt; }
}
// If airborne update
if (o.airborne) {
o.vy += 980*dt;
o.vx *= Math.pow(0.99, o.drag*dt*60);
o.vy *= Math.pow(0.995, o.drag*dt*60);
o.x += o.vx*dt; o.y += o.vy*dt; o.angle += o.av*dt;
if (o.y >= floorY - o.r) {
o.y=floorY-o.r;
if (Math.abs(o.vy)>120) o.vy = -o.vy*0.25; else o.vy=0;
o.vx *= 0.72;
if (Math.abs(o.vx)<8) {o.vx=0; o.grounded=true;}
o.av *=0.75;
}
if (o.x < o.r) {o.x=o.r; o.vx=Math.abs(o.vx)*0.35;}
if (o.x > W-o.r) {o.x=W-o.r; o.vx=-Math.abs(o.vx)*0.35;}
if (o.y > H+100 || o.x>W+100) o.dead=true;
}
}
remove dead.
}
function airborneFromBreach(o,vx,vy) {
o.airborne=true; o.vx=vx; o.vy=vy; o.grounded=false; o.av=rand(-6,6); o.drag*=0.6;
}

Need note object y is center? For fish/rock. For plant, root. Good.

Need draw:
function drawBackground() {
gradient sky? room. Draw floor, stand.
}
function drawWater() {
ctx.save(); clip to tank interior;
let grad=ctx.createLinearGradient(0,tank.y,0,tank.bottom); colors...
ctx.fillStyle=grad;
ctx.beginPath();
const n=80; for i 0..n: x=tank.x+8+i/n*(tank.w-16); y=waterY + Math.sin(i*0.4+t*0.002)*3 + Math.sin(i*0.09+t*0.001)*1.5; if i==0 moveTo; lineTo. Then lineTo right bottom...
fill.
// bubbles?
ctx.restore();
draw waterline:
ctx.beginPath(); ... stroke;
// maybe label.
}
function drawTankGlass() {
ctx.save(); stroke glass; highlights; maybe cracks?
}
function drawCrackOpening() {
if (!bursting) drawCrack(); else drawOpening();
}
function drawCrack() {
const len=30; ctx.save(); shadowBlur=12; strokeStyle red; lineWidth=3; beginPath; move right-6, crackY-len; for i 0..len/3: y=...; x=right-6+sin(i*1.7)*4+...; lineTo; stroke; draw small red ticks.
}
function drawOpening() {
const h=opening; const x=tank.x+tank.w-5; ctx.save();
ctx.fillStyle='rgba(5,10,15,.75)';
ctx.beginPath(); ctx.moveTo(x+8, crackY-h/2); for i=0..12 { y=crackY-h/2 + i/12*h; ctx.lineTo(x + (i%2?8:-3)+sin(i*1.4+burstTime*12)*5, y); } ctx.lineTo(x+8, crackY+h/2); close; fill;
stroke...
}
function drawObjects() {
// maybe draw underwater objects inside tank; airborne after?
objects.forEach(o=>drawObject(o));
}
function drawObject(o) {
ctx.save();
if (o.airborne || !isInsideTank(o.x,o.y)) ctx.globalAlpha=0.95;
if (o.type==='fish') {
// translate to o.x,o.y, scale. Draw body. angle maybe.
} else ...
}
function drawParticles() {
water droplets before puddle? Draw particles after objects maybe.
for p of particles: draw circle with gradient maybe.
for puddle: draw circles.
}
function drawShards() {
for s of shards draw polygon rotated.
}
function drawCurrentArrows() {
if (!bursting && flow>0? maybe draw faint arrows? But before failure no flow. Draw current in tank near breach when bursting:
const flow=flowStrength(); for i=0..5: y=crackY-80+i*30; x=tank.x+tank.w-120; draw arrow with alpha flow*0.5.
}
function drawHUD? HTML overlay. Maybe draw readout in canvas? Not necessary but could.
}

Need UI update:
const hudHead=document.getElementById('head'), etc. In loop set text.
Need if not bursting, readout. Buttons.

Need maybe `canvas.addEventListener('pointermove', e=>{ pointer; if (bursting) return; const near = Math.abs(eY-tank.x-tank.w) < 25 && eY>tank.y+5 && eY<tank.bottom-5; dragging=near; canvas.style.cursor=near?'ns-resize':'crosshair';});`
But pointer event coordinates need convert.
`function eventPoint(e){ const rect=canvas.getBoundingClientRect(); return {x:(e.clientX-rect.left)*W/rect.width, y:...};}`
mousedown if near crack and !bursting set dragging.
mousemove if dragging crackY=clamp(eY,...)
touch? pointer.
Buttons.

Need maybe reset button.

Potential issue "currentAt" uses performance.now inside update; okay.

Need maybe ensure `dt` clamp to 0.033 to avoid huge.

Need maybe "flow strength based on water depth" use waterline-crackY. "lower crack should initially produce stronger jet" yes.

Need maybe "objects transitioning correctly from underwater motion to airborne motion and then floor collisions": fish/rock/toy. Plant anchored. Good.

Need maybe "Fish attempting to swim against current before being swept through breach": fish update. Maybe draw fish tail wiggling. Good.

Need maybe "Glass fragments with angular velocity, collisions, and water resistance": shards. Good.

Need maybe "water escaping through opening with flow strength based on water depth and decreasing as tank drains": water particles and waterY. Good.

Need maybe "curved water jet affected by gravity": water particles gravity. Good.

Need maybe "puddle collides with room boundaries": puddle walls. Good.

Need maybe "visible waterline lowers continuously rather than disappearing all at once": waterline draw. Good.

Need maybe "drag crack vertically before triggering failure": pointer. Good.

Need maybe "large glass aquarium": draw tank large.

Need maybe "side panel develops visible crack and then bursts": crack pre-burst, opening. Good.

Need maybe "single html file" present. We'll write.

Let's create file with code.
write {}
assistant
thinking
We need answer final with file maybe after creating index.html. Need build single page web app all in one file. Need inspect current dir? We need create index.html. Need likely use HTML Canvas. Need implement simulation satisfying detailed requirements. Need no external resources. Need code self-contained. Need maybe deliver concise. We need generate large code. Need use tools. We'll plan and implement.

Need create /tmp/ws/index.html. Need maybe use Canvas with 2D physics. Requirements:
- glass aquarium side panel develops visible crack and bursts.
- water escaping through opening with flow strength based on water depth and decreasing as drains.
- curved jet affected by gravity.
- spreading puddle collides with room boundaries.
- fish, rocks, plants, floating toy reacting differently according to density, buoyancy, drag, current.
- objects transitioning from underwater motion to airborne motion then floor collisions.
- fish attempting to swim against current before being swept through breach.
- glass fragments with angular velocity, collisions, water resistance.
- visible waterline lowers continuously.
- user drag crack vertically before triggering failure. Lower crack stronger jet.
Need one HTML file. Could use canvas. Need include UI instructions. Need robust simulation.

Need design:
Canvas with room floor, aquarium tank occupying x=80..520, y=80..520, water top y=150 maybe. Crack draggable on right side panel. Before failure: crack drawn as jagged line at selected y, on tank side. User drags vertical. Button "Trigger burst". On trigger: breach opens. Crack grows into opening, burst glass fragments. Water exits right side through opening.

2D simulation:
Coordinate: world canvas pixels. Tank boundaries: left x=90, right x=520, floor y=520, top y=80. Water initial top y=145. Depth = waterY - crackY? Actually water depth above crack = waterY - crackY? In canvas y downward, water top is y=130; crack at y=350, depth=220 px. Flow velocity horizontal outward = sqrt(2*g*depth) * coefficient maybe. Flow rate = width * velocity * drag. As tank drains, waterY rises (larger y). Need waterline lowers continuously: waterY increases (lower in screen) over time. Need draw water.

Water escaping: particles from opening at right side x=tankRight, y=crackY, with velocity vx = flowSpeed + random, vy=random. Flow speed based on sqrt(depth), depth = waterY - crackY. Flow rate decreases as depth decreases. Particles have gravity, drag, bounce off floor/walls/room boundaries, collide with floor and boundaries. Puddle spreading: Use particles; also maybe puddle mesh/ellipse grows near floor. Need collisions with room boundaries: floor y=600, left/right walls. Use particles bounce/damp.

Objects:
Fish: underwater objects with swimmer behavior. Each has position, velocity, type, density/buoyancy/drag. Fish target to swim against current; fish have "against current" force opposite water current near breach. They swim left (against rightward current). If water current strong, swept right through breach. They need transition underwater->airborne->floor collision: when crossing tank right boundary or water surface? Actually objects exit through breach in water jet and become airborne; then collide floor. Need implement. Rocks/plants/toy:
- Rocks: dense, sink, roll on floor, affected by current less, maybe dragged by water.
- Plants: buoyant flexible, float, sway, current drag.
- Toy: floating, high buoyancy, bobbing.
Need density, buoyancy, drag properties.

Simplify:
Objects array with x,y,vx,vy,r,type, density, buoyancy, drag, angle, angularVelocity. Each frame:
- Determine inside water: y < waterY and x inside tank.
- If inside water and not breached:
* vertical forces: buoyancy = (1-density?) maybe upward if density<1. We need different. Use netBuoyancy = (1-density)*gWater? Fish density ~0.98 near neutral; toy 0.25; plant 0.5; rock 2.4. Upward force = (1-density)*something. Fish swim.
* drag with current: current field near breach (rightward) and general flow to breach. Use waterCurrent(x,y): if waterY-crackY>0 and breachOpen, u = (1 - dx/tankWidth)^2 * min(depth/tankHeight?)? plus jet region. Add to object velocity.
* fish attempt swim against current: if breachOpen and x > tankRight-80 or always near breach, fish target left, swimForce - maybe if current strong, if they survive. They may be swept through breach when x > tankRight and y between breach bounds or water line. Need open breach area.
* when breachOpen and object near right wall and y within crack opening vertical range (crackY-20..crackY+30 maybe growing), and x> tankRight-20, if water current > threshold, set state airborne, vx=flowSpeed*1.2, vy=random, x=tankRight+2, y=crackY+random.
* If underwater and not exiting, move with swimming/current.
- If airborne:
* gravity, air drag, no buoyancy; collisions with room boundaries floor/walls; maybe if x left/right clamp.
* If object hits floor, set y=floor-r, vy=-vy*bounce, vx*=friction, angle spin. If inside tank? Need floor outside? Aquarium floor at y=520. Puddle on floor outside at y=575? Room boundaries include floor y=590.
* Airborne objects from breach arc to floor outside tank (x>right). Some rocks/plant/toy.
- If underwater but tank drained? waterY lower than object? If y > waterY+? object emerges? Need transition from underwater to airborne maybe when waterline passes? But requirement objects transitioning correctly from underwater motion to airborne motion and floor collisions. We can model breach exit. Also if waterline below object? In real object floats/sinks; but not necessary. But maybe toy emerges when waterY rises above it, rocks floor. We can include if y > waterY and not airborne: if y > waterY+10 and object is dense maybe sink to floor; if buoyant and y>waterY+10, move upward until water surface. Not airborne.
- Floor collisions: For underwater objects, tank floor at y=520; if y+r>520, set y=520-r, vy=0, friction. Rocks/plant on floor. Airborne objects outside tank floor at y=590.

Need glass fragments:
After burst, create shards around crack/right panel. Each has x,y,vx,vy,angle,omega, size, shape vertices, mass, color, water resistance. They should collide with tank boundaries, floor, room boundaries? Glass fragments with angular velocity, collisions, water resistance. Need draw shards as polygons. They are initially at right wall, then fly outward, bounce off floor/walls, some fall into water (if x in tank?) and damp by water resistance. Need collision:
- Boundaries: left x=tankLeft+? maybe not; right tankRight; floor=520; room boundaries left/right/floor. For shards: if x > tankRight+size? bounce off room right; if y>floorY? bounce. If x < tankLeft? bounce. If y > waterY and x inside tank? water resistance. Also if y<waterY maybe add water drag.
- Angular velocity decays with air drag and water resistance.

Need waterline visible:
Draw water surface as line from left wall to right wall, with wave. Lower continuously: waterY += drainRate*dt. Also water depth decreases. Need maybe draw water fill inside tank clipped. Draw top line.

Need crack dragging:
Mouse down on crack or tank side? Provide canvas pointer events. Crack draggable vertically only on right panel. Maybe if near crack within 25 px or x around right panel. Show crosshair. Lower crack stronger. Button triggers. Also maybe auto reset.

Need code:
HTML with style, canvas, UI overlay. Need no external resources.

Need ensure code works. Let's design.

Canvas size maybe 900x650. CSS full viewport. Use devicePixelRatio scaling. JS:
const canvas, ctx; resize to container. But fixed world dims maybe 900x650. Use CSS width 100%, height auto. For pointer mapping use rect.

World constants:
const W=900,H=650;
const tank={x:90,y:90,w:430,h:430}; floorY=585; room={left:40,right:860,top:25,bottom:590};
waterY=145; crackY=330; breachOpen=false; burstTime=0;
openingHalf=12 before? After burst, openingHalf = 22+sin? maybe grows.
waterDepth = waterY-crackY.
flowBase = sqrt(max(0,waterDepth))*0.22 maybe px/s? Need velocities. If depth=200, sqrt=14, *0.35=5 px/s too slow? Need visible. Use vx = 120 + sqrt(depth)*50? Actually water pressure speed ~sqrt(2gh), in px/s maybe sqrt(2*980*200)=626 px/s. Need scale. Use flowSpeed = 120 + Math.sqrt(depth)*18 => 120+254=374 px/s. Good. flowRate = flowSpeed * openingWidth * 0.025 maybe particles per sec. openingWidth=40. At 374, rate=374*40*0.025=374 particles/s, maybe high but okay. Use dt.

Water particles:
particles array {x,y,vx,vy,r,life,type}. Spawn at breach x=tank.x+tank.w, y=crackY+rand. OpeningWidth maybe 36. vx = flowSpeed*(0.8+rand*0.4)+rand; vy=rand(-60,60). r=3-5. On update:
- x += vx*dt; y += vy*dt; vy += gJet*dt (980? px/s² maybe 1100); vx *= 0.995; if y > floorY- r: y=floorY-r; vy=-vy*0.15; vx*=0.8; if vx < 5 remove; if x>room.right or life>4 remove.
- if y<waterY? if particle inside tank and y<waterY? maybe it is water, but after exit outside; if x<right and y<waterY maybe stay? Not needed.
- Puddle: draw particles as small blue circles; also draw puddle ellipse at floorY? Use puddleRadius grows based on water particles count. Maybe maintain puddleSpread variable. Increase when particles hit floor: puddleSpread += 8*dt*(1+count?). Limit by boundaries. Draw translucent blue ellipse/irregular puddle at floorY-5 with radius puddleSpread, clipped to room. Collides with boundaries: if spread > floorX distance, clamp? Use boundaries:
puddle = {x: tank.x+tank.w+10, y: floorY-4, radius:0, spread:0}; When particles hit floor, puddle.spread = min(maxDist? maybe Math.max(puddle.spread, sqrt((x-puddle.x)^2+(y-puddle.y)^2))). Limit by distance to left/right/top walls: maxR = min(puddle.x-room.left, room.right-puddle.x, floorY-room.top-30); puddle.spread = min(puddle.spread + dt*20, maxR). Draw ellipse radius. Need "spreading puddle collides with room boundaries": draw puddle clipped and maybe text "puddle pressed against left/right boundary"? We can implement boundary clamping: puddle.spread = Math.min(puddle.spread + dt*8, min(puddle.x-room.left-10, room.right-puddle.x-10)); draw.

Objects:
Types:
fish: x around 180-430, y 180-430, r=14, density=.98, buoy=.02, drag=1.2, color.
rock: x 250, y 490, r=20, density=2.2, buoy=-1.2, drag=.5, mass, color.
plant: x 330, y 450, r=16, density=.45, buoy=.55, drag=.8, length.
toy: x 450, y 220, r=22, density=.2, buoy=.8, drag=.4, color.

Maybe multiple fish. Each fish has phase, swimStrength, active. Need fish attempting to swim against current before swept. Implement:
updateObject(obj, dt):
if obj.state==='airborne': updateAirborne
else updateAquariumMotion.
AquariumMotion:
- current = getCurrentField(obj.x,obj.y); // vector right/up maybe.
- if obj.type==='fish':
let swimDir = -1; // swim left
// If near breach, fish tries to orient and swim left; maybe if water current strong, it can still be swept.
let targetForce = 45; // px/s²
let currentX = current.x;
if (breachOpen && obj.x > tank.x+tank.w-80) {
// "attempting to swim against current": apply left force proportional to current and lower crack
obj.vx -= targetForce * Math.min(1,currentX/450) * dt;
obj.vy += Math.sin(time*4+obj.phase)*20*dt;
// if current > target, they may be swept
} else {
obj.vx += (-30 + Math.sin(time*2+phase)*10)*dt; // swim left
obj.vx *= (1 - 0.25*dt)
}
- buoyancy: net = (1-density)*200 maybe upward? For fish density .98 net .02*200=4 px/s² upward. Need keep in water. Add swim vertical.
- water drag: vx *= exp(-obj.drag*0.6*dt), vy *= exp(-obj.drag*0.5*dt); Add current to velocity: vx += current.x * (1 - Math.exp(-obj.drag*dt))? For fish less current maybe; toy more.
- If breachOpen and near breach:
const openingTop=crackY-openingHalf, bottom=crackY+openingHalf;
if obj.x > tank.x+tank.w-15 && y between top-10 and bottom+10 && current.x > 140:
enterAirborne(obj, flowSpeed*1.3, vy+rand(-40,40));
- else if y+r>floorY-? inside tank: floor collision.
- if y-r<waterY-5 and obj.type not fish? maybe float. For toy/plant, buoyancy upward.
- if y+r>waterY+20 and obj.buoyant? maybe move upward.

Need water current field:
function getCurrentField(x,y) {
const depth = Math.max(0, waterY-crackY);
if (!breachOpen || depth<=5) return {x:0,y:0};
const dx = tank.x+tank.w - x;
const insideX = tank.x < x && x < tank.x+tank.w;
const vertical = Math.max(0, 1 - Math.abs(y-crackY)/(Math.max(200, depth*0.75)));
const pressure = Math.sqrt(depth)/Math.sqrt(initialDepth) maybe.
let xVel = insideX ? pressure * 120 * (dx/tank.w)^2 * vertical : 0;
let yVel = insideX ? (crackY-y)*0.08*pressure*vertical : 0; // flow towards crack
return {x:xVel, y:yVel}
}
Maybe near breach strong.

Flow rate decreasing as tank drains: spawn probability based on flowSpeed. Also opening width maybe after burst:
openingHalf = 24 + Math.min(18, burstTime*5?) maybe initially 20, grows to 42.
openingWidth=42.

Waterline lowering:
drainVolume = flowRate*dt. Need update waterY:
waterY += drainRate*dt. drainRate maybe flowRate * 0.003? If flowRate ~300 particles/s, 0.003 -> .9 px/s. Need visible. Use waterY += flowRate * 0.006 * dt; if flowRate=400 => 2.4 px/s. As tank drains to floor maybe ~100s. Good. But if waterY rises too fast? okay.
Also spawn particles count based on flowRate; if waterY-crackY<=0 no spawn.
Need maybe waterline draw before/after.

Draw:
- Background room: walls, floor.
- Draw tank glass: outline rounded rect, highlights.
- Draw water fill clipped inside tank:
ctx.save(); clip rect tank interior; fill gradient from top to bottom blue; water top y waterY; Draw wave line at waterY.
Maybe draw waterline outside? "visible waterline": draw thick line along tank side at y=waterY, label "waterline". Draw smaller tick marks.
- Draw sand floor inside tank.
- Draw crack: before burst: jagged line at crackY on right wall, red glow; maybe text "drag crack up/down". Draw side panel with crack. After burst: opening rectangle/irregular hole at right wall, shards.

- Draw water particles.
- Draw objects:
Fish: draw body ellipse with tail, eyes, orientation based velocity; if underwater. If airborne, draw in air maybe.
Rock: irregular polygon.
Plant: stem(s) swaying based current; buoyant.
Toy: boat/ball? Draw yellow rubber duck maybe with body circle, beak, wing; bobbing.
Glass fragments: draw polygons.
- Draw jet: water particles and maybe blue stream line.
- Draw puddle.

Need UI:
Controls: draggable crack. Button trigger. Reset. Maybe status text: "Drag crack vertically on right glass. Trigger burst." "Current crack depth: ... Flow: ...".
Need pointer events:
let dragging=false;
canvas.addEventListener('pointerdown', e => {pos; if in side panel x between tank.x+tank.w-10 and tank.x+tank.w+10 and y between tank.y+20 and floorY-20 or near crack: dragging=true; set crackY; canvas.setPointerCapture;});
pointermove if dragging: clamp crackY between tank.y+25 and floorY-40.
pointerup false.
Maybe allow click on tank right side to set crackY, not only crack.
Trigger button.

Need "side panel develops visible crack and then bursts" maybe show crack grows after trigger. Use crack line before trigger. After trigger, opening. Good.

Potential issue: Need create single file. We can write code.

Let's implement with careful syntax. Need maybe use class? Simple.

Need maybe include meta, style.

Need handle high DPI. Set canvas.width = W*dpr, height=H*dpr; ctx.setTransform(dpr,0,0,dpr,0,0). But CSS? We'll set style width: min(100vw, 960px); height:auto; canvas width W. Need pointer mapping with rect.

Need maybe use requestAnimationFrame.

Let's draft code. Need maybe large but not too huge. Use write.

Need ensure no external resources. Use system fonts.

Let's write index.html.
write {}
assistant
thinking
We need respond to user: create large glass aquarium single-page web app, one self-contained index.html in current dir /tmp/ws. Need implement simulation. Need inspect? likely empty. Need create file. Need produce final with path. Need ensure no external resources. Need likely use Canvas 2D simulation. Need include all features. Need maybe use JS physics simple. Need implement user drag crack vertically before triggering failure. Need crack visible, trigger perhaps after drag? Need "drag the crack vertically before triggering the failure." Could have drag handle to move crack along side; click "Trigger Failure" or drag and release triggers? Need implement.

Need build robust enough. Need write index.html. Need not separate. Need maybe include CSS style and JS script inline. Need no external resources. Need features:
- Aquarium with water, visible waterline lowers continuously.
- Side panel crack and burst: initial crack vertical, user drag vertically before triggering. Lower crack initially stronger jet than higher due water depth.
- Water escaping through opening with flow strength based on water depth and decreasing as drains.
- Curved water jet affected by gravity.
- Spreading puddle collides with room boundaries.
- Fish, rocks, plants, floating toy reacting differently density buoyancy drag current.
- Objects transitioning underwater->airborne->floor collisions.
- Fish attempting to swim against current before swept through breach.
- Glass fragments angular velocity, collisions, water resistance.
- visible waterline lowering continuously.
Need implement in HTML.

Let's design.

Canvas full viewport. Aquarium rectangle maybe x=120 y=110 w=760 h=500. side panel right side? crack on side panel at x = aquarium right glass x - 5 maybe. Crack vertical segment draggable along right side. Trigger failure when click/touch "Trigger Burst" or after drag release? User asks drag crack vertically before triggering. We can have "Drag crack upward/downward on right wall, then trigger burst." Maybe trigger button. Also if drag starts and release? Could have "Trigger failure" button. Need no external.

Simulation physics:
Canvas 2D. Coordinate pixels. Aquarium interior bounds: left x=120, top y=110, bottom y=610, right x=880. height=500.
Initial water height maybe 450 px from bottom? y=160. waterline.
Crack: x=right - maybe 875, y = draggable. length = 50. Initial maybe 360? User drag vertical. Opening radius/width grows after trigger based on water depth at crack. Flow strength = sqrt(max(0, waterDepthAboveCrack? water pressure = rho g (waterlineY - crackY))) maybe if crack below waterline; if above no jet. Lower crack -> more depth. Opening area grows with pressure and time. Burst: side panel breaks, crack length increases maybe to 120; water leaks through opening. Flow rate = area * sqrt(2*g*(waterlineY-crackY)). We need water level lowers continuously. Need update water height based on flow. Since tank drains to bottom maybe waterline y increases toward bottom. Use outflow volume per second = C * area * sqrt(depth). Volume decrease = outflow * dt, water height decrease = volume/(aquarium area). In pixel units arbitrary. Need avoid waterline above crack? if crack above waterline no flow. If waterline falls below crack, outflow stops. But if crack lower, drains more. Need lower crack initially stronger.

Water particles/jet:
Spawn water droplets from opening at x = right+8, y=crackY. vx positive based on pressure maybe 200-800, vy maybe small. Gravity. Air particles with gravity, bounce floor, evaporate or puddle. Need puddle spreading on floor collides with room boundaries. Represent puddle as ellipse/rect on floor (floor y=bottom+? Actually floor beneath aquarium at y=650). Aquarium bottom at 610. Puddle on floor from x=right+10 to boundaries x=20..width-20, y=bottom+20? Need collision with left/right walls/room boundaries. We'll draw puddle as filled semi-transparent blue ellipse/shape along floor, with radius expanding based on water particles landing. It should collide with room boundaries: if left/right limit. Could draw puddle between boundaryX and edge. Use variable puddleLeft, puddleRight, puddleY, puddleDepth maybe. It expands each frame by water flow/landed. Clamp to room boundaries. Need maybe show puddle under tank to left? Opening on right side, puddle spreads right; boundaries are left/right walls. Could include room floor, left wall at x=20, right wall at canvas width-20. Puddle center/right expands until wall. Use "floor boundary" collision.

Objects:
Fish, rocks, plants, toy. Need underwater motion and current. Aquarium objects. Use particle-like physics. Types:
- fish: buoyant near neutral, swim force, density, drag. Underwater: horizontal current from breach: flow velocity near opening (rightwards) maybe decreases with distance from breach and water depth. Fish attempt to swim against current (left) when inside and before swept. If current strong, swept right. If breach open and fish near opening, waterline maybe lower; objects near opening can be ejected through breach into air? Requirement "objects transitioning correctly from underwater motion to airborne motion and then to floor collisions". Need implement for fish/toy/rocks/plants maybe. When breach open, strong current near opening can pull objects. If object x > breachX and y within opening range, launch them out through breach: position x = right+some, y=crackY + offset; velocity vx positive, vy maybe negative/positive based on current/updraft; then gravity. They land on floor/puddle. Need simulate airborne for all ejected objects, then floor collision. Rocks dense sink, plants float/floaty, toy floats. Need object transitions:
- Underwater: fish, rock, plant, toy in tank. Forces: gravity/buoyancy based on density relative to water (density maybe 0.5 toy float, rock 2.2 sink, plant 0.9 float, fish 1.0). Drag. Current. Fish swims left. Plant buoyant and sways. Rock sinks.
- When ejected: if near breach and water pressure high enough, set inAir=true, position outside, velocity. Then gravity, air drag, floor collision at floorY=bottom+24. If object hits floor, bounce/damp and rest.
Need "reacting differently according to density, buoyancy, drag, current": Show perhaps labels and different motion. Objects have density, drag, buoyancy, color, shape. Fish swim against current with fishTail animation. Rock sinks fast. Plant floats/sways. Toy floats and gets pushed.

Glass fragments:
After burst, create shards. Each with x near breach, y along crack, vx positive, vy random, angular velocity, rotation, size, shape. Collide with floor, aquarium bottom, maybe walls? Need angular velocity, collisions, water resistance. Shards in air outside and water? Some fall through opening into puddle/floor. Need water resistance if inside tank? We'll simulate all shards with gravity, air drag; if y > bottom maybe floor collision; if y<top bounce? If y near right wall? Could collide with floor and left/right room boundary maybe. Need water resistance if shard y is inside tank water region (y between waterline and bottom) and x inside aquarium? But after ejection mostly outside. Some fragments may fall into tank water; reduce velocity. Need collision with floor/puddle. Also maybe with aquarium bottom: y >= bottom - size -> bounce with damping. If x outside and y at floor -> floor collision. Need angular velocity.

Crack:
Draw side panel with crack line. Before trigger, crack visible vertical jagged line; draggable. User can drag handle. Lower crack stronger. Trigger failure: button. Could also start if drag handle moved? We'll implement both: drag handle, click button or press Space. Need maybe "dragging" status.

Need single file. Need maybe include UI overlay with instructions, status, reset. Need style glass aesthetics. Need no external fonts.

Need code robust. Let's think through physics details.

Canvas setup:
const canvas, ctx, dpr, resize. Use device pixel ratio. CSS full. Need coordinate system in CSS pixels; set canvas.width=innerWidth*dpr etc; ctx.setTransform(dpr,0,0,dpr,0,0). Use W,H.

Room:
leftWallX=24, rightWallX=W-24, floorY=H-40 maybe. Aquarium bottom should be above floor. In resize, compute aquarium:
baseW maybe min(860, W-260), baseH min(520, H*0.62). But simple:
if W<900, scale=0.75? Need responsive. Could compute:
A = {x: Math.max(40, W*0.08), y: 80, w: Math.min(820, W*0.78 - 120), h: Math.min(520, H*0.68)}; if W small adjust.
Need ensure W-24 right wall. Let's set:
const margin=30;
const aw = Math.min(820, W - margin*2 - 120);
const ah = Math.min(520, H - 210);
const ax = margin;
const ay = 70;
const bottomY=ay+ah; floorY=bottomY+35; if floorY > H-30, set floorY=H-30; ah=floorY-bottomY maybe.
Need right side.
Waterline initial = bottomY - ah*0.88? y = bottomY - 440 maybe. If ah 520, waterline 150.
Crack y initial = bottomY - 300 maybe.
Draggable handle at crackY.

Room boundaries: left x=20, right x=W-20, floorY. Puddle y=floorY+5? Draw on floor. Puddle spread:
let puddle = {left: right+10, right: right+10, depth: 0, maxRight: W-25};
Each frame after burst, outflow maybe adds volume. Puddle expands by outflow*dt*0.8; left maybe right+10; right = min(W-24, puddle.right + outflow*dt*0.025 + waterParticlesLanded*0.03). If no flow, maybe spreads slowly? Need collides with boundaries. Draw puddle as blue shallow ellipse/ribbon on floor from left to right with height = min(22, 4+depth*0.05). Also water particles landing update puddle.
Could also spawn puddle droplets? water particles landing set.

Water particles:
Array {x,y,vx,vy,r,life}. Spawn while burst open: spawnRate = 150 + flowStrength*0.08? Need cap. FlowStrength normalized.
Each particle:
x = right + 2; y = crackY + random(-opening/2, opening/2) maybe opening grows.
pressure = max(0, waterlineY - crackY). If pressure > 0. flow = openingArea * sqrt(pressure) * 0.05? Use outflowVolume = flow*dt; waterlineY += outflow / tankArea. Need arbitrary.
vx = 150 + sqrt(pressure)*4 + random; vy = random(-50,50) maybe.
gravity = 900 px/s^2. Air drag.
If x > right+120 and y >= floorY-r -> land, spawn puddle splash, add puddleDepth.
If y > bottomY? If inside tank? But particles outside. If y >= floorY and x between ax and right? maybe splash.
Particle life 4s.

Waterline lowering:
TankArea = aw*ah. outflowVolume maybe in px^3/s. Need calibrate. pressure = waterlineY - crackY (positive if water above crack). pressure depth. flow = 0.000000? Let's use units:
openingArea = Math.PI*(openingRadius)^2? Opening radius = 4 + sqrt(pressure)*0.2? Pressure depth maybe 0-400. opening=4+sqrt(depth)*0.4 maybe 12-24. Flow volume = opening*sqrt(depth)*120? If depth=300, sqrt=17.3, opening=16 => 33k px^3/s. Tank area=760*520=395k, waterline rises 0.084 px/s? too slow. Need stronger. Use 1500? 33k*1500=50M, /395k=126 px/s. Good. Let's use outflow = opening * sqrt(depth) * 900. If burst open and crack below waterline. But if lower crack depth 330, outflow ~16*18*900=259k px/s, waterline drop 0.66 px/s. Maybe okay. Opening grows with time.
opening = 4 + min(28, Math.sqrt(depth)*0.35 + burstProgress*20). burstProgress increases 0..1.
FlowStrength = clamp((opening*sqrt(depth)*900)/(maxFlow), 0,1). maxFlow ~ 200k.
Waterline y += outflow / (aw*ah) * dt. Since y increasing = lower. If waterline >= bottomY - 10, set waterline=bottomY-10.

But waterline lower continuously. Need draw waterline with surface. Maybe waterlineY.

Objects physics:
Define objects:
function createObject(type)
Types:
fish: x=250+random, y=waterline+random, vx=-20, vy=random; density=1.0, drag=0.8, mass=1, swim=45, color.
rock: x=350, y=bottom-80, density=2.2, drag=0.25, mass=1.5, color brown, radius 18, angular velocity.
plant: x=520, y=bottom-150, density=0.75, drag=1.2, mass=0.8, height=110, color green.
toy: x=620, y=waterline+20, density=0.45, drag=0.55, mass=0.6, color yellow, radius=22.
Maybe multiple fish. Need "fish" plural. Add 5 fish. Plants maybe two. Rocks maybe 3. Toy one.
Object update:
If !ejected:
- Determine current at object x,y. Current stronger near breach and opening; flow direction x positive. currentX = pressure>0 ? exp(-distanceToBreach/180) * (100 + flowStrength*260) * (1 - (y-waterlineY)/waterHeight*0.2) : 0. Maybe also downward/upward near opening. If crack open, current near breach pulls right. Fish attempt to swim against current: if type fish and not ejected, target vx -= 40 + fish effort; else maybe natural.
- Forces:
density relative to water. buoyancy = (1 - density) * 600? For density<1 positive upward? Actually y positive down. If density 0.45, buoyant force upward = -(1-density)*600; if density>1 downward = -(density-1)*600? So vy += (1-density)*450.
drag: vx *= exp(-drag*dt), vy *= exp(-drag*dt).
- Type-specific:
fish: tail phase, swim force left = -fish.swim, random wiggle; if current strong and x > right-120 and y within opening range, accelerate right; if x > right-40 and |y-crackY| < opening*1.2 and pressure>0, eject.
rock: sink downward if above bottom, bounce bottom.
plant: buoyant, sway horizontal with sin time, long stem.
toy: floats at surface, bobbing, pushed by current, if near breach eject.
- Boundaries: x clamp ax+radius to right-10. y clamp waterline+radius to bottomY-radius. If ejected false.
- Eject condition:
const nearOpening = Math.abs(obj.y-crackY) < opening*1.3 && obj.x > right - 30 && pressure > 0;
if nearOpening and flowStrength > 0.25 and obj.x > right - 50 maybe:
ejected=true; obj.x=right + random(0,20); obj.y=crackY+random(-opening*0.35, opening*0.35); obj.vx=180+flowStrength*350+random; obj.vy= random(-60,60) - (obj.y-waterlineY)*0.1?; obj.inAir=true; maybe if toy still buoyant? But "objects transitioning ... airborne" yes.
- If ejected:
vx *= exp(-airDrag*dt), vy += gravity*dt, vy *= exp(-airDrag*0.4*dt); x += vx dt; y += vy dt; if y >= floorY - size => y=floorY-size; vy=-vy*0.15; vx*=0.75; if abs(vy)<20 and onFloor then rest; maybe slide with vx decel. If x < leftWallX+size or x > rightWallX-size => bounce/clamp. If x between right+? floor. If y < ay? maybe bounce top.
Need draw ejected objects at outside, perhaps falling to floor. Fish shape oriented.

Need "Fish attempting to swim against current before being swept through breach" perhaps draw fish face left, tail, label "swims left", if near breach then swept. Could include small arrows. Need maybe fish fish object has swimDirection=-1; current pushes right. Good.

Glass fragments:
Array shards. On burst, generate maybe 90 shards. Each with x=right+random(-5,5), y=crackY-random(length/2,length/2), vx=100+random*350, vy=random(-250,150), size=random 6-18, angular velocity random -12..12, rotation, color, shape random polygon. Update:
vx *= exp(-0.25*dt) (air/water resistance), vy += gravity*dt; maybe if inside tank water and x between ax and right+? apply water resistance 2.5 and upward buoyancy? For fragments in water (x inside tank and y below waterline) vx *= exp(-3dt), vy += (1-density? maybe 0.8) upward? density 2.5 so net downward maybe. But fragments outside mostly.
Collisions:
if y > floorY - size => y=floorY-size; vy*=-0.15; vx*=0.75; angular velocity *=0.8; if |vy|<25 vy=0;
if x < leftWallX+size or x > rightWallX-size => x=boundary; vx*=-0.35; vy*=0.9; angular*...
if y < ay+size? bounce top maybe.
if y > bottomY and x between ax and right? bottom collision.
Water resistance if x>=ax && x<=right && y>=waterline && y<=bottom: vx*=exp(-3dt), vy*=exp(-2dt)
Draw shards as polygon rotated, with bright edges.

Crack:
Draw aquarium glass with gradients, water inside, air above. Need side panel. Aquarium rectangle with glass border. Draw water fill:
ctx fill inside rect with blue gradient from waterline to bottom. Draw waterline as wavy line at waterlineY. Need visible waterline lowers. Use sin wave.
Draw glass side panel: translucent white border, highlights. Crack on right side:
Before burst: draw vertical jagged crack with dark line, draggable handle circle. After burst: draw jagged broken opening, shards, missing glass.
Opening shape after burst: width = opening maybe. Draw black/blue opening ellipse/irregular. Need if waterline above crack. If waterline below, no jet. But crack still visible.

Water escaping:
spawn water particles. Draw maybe stream line? Particles enough. Could draw a translucent blue jet curve from opening to floor with alpha, based on outflow. Need "visible water jet affected by gravity" maybe draw particles and maybe a curve. We can draw a semi-transparent blue curved stream path from x=right+5,y=crackY to x = right+vx*t, y=crackY+vy*t+0.5g t^2; maybe sample t 0..0.7, stroke. But particles show.

Puddle:
Draw floor (gray) with boundary lines. Puddle ellipse/ribbon:
let puddleLeft = right+5; puddleRight = min(rightWallX-10, puddle.right). Draw gradient blue fill from left to right with width. Maybe y=floorY+10; height = 10+puddle.depth*0.05. Draw at floor. It collides with boundaries: if right reaches rightWallX, stays. left starts right+5. If waterline stops, no expansion.
Maybe also draw water particles landing.

Room boundaries:
Draw left wall at x=20, right wall at W-20, floor. Puddle collides. Need maybe draw floor and walls.

UI:
Overlay top left with title, instructions, status, controls:
- "Drag the jagged crack on the right glass, then Trigger Burst (Space)."
- "Crack depth: X cm / Flow: Y% / Waterline: Y%"
- Buttons: Trigger Burst, Reset. Maybe "Lower crack" etc.
- Maybe slider? User specifically drag crack vertically. We'll implement handle.
Need maybe touch support.

Need ensure all inline. Need no external. Need maybe if canvas not supported.

Need create index.html. Need maybe use JS. Need maybe include comments. Need maybe use exact path.

Let's craft file carefully. Need test in browser? We can run maybe no browser. We can do static syntax check with node? HTML with script; can extract? Maybe use `node --check` on script? But script uses browser DOM; node check syntax okay. Need maybe no syntax errors. Use bash to write file? We can use write tool. Need maybe no separate files. We'll write.

Need think code in detail.

Let's draft.

HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Cracking Aquarium Burst Simulation</title>
<style>
html,body { margin:0; height:100%; overflow:hidden; background:#07111f; font-family: system-ui,...; color:white; }
canvas { display:block; width:100vw; height:100vh; }
.panel { position:fixed; top:16px; left:16px; max-width:360px; padding:14px 16px; background:rgba(5,15,28,.62); border:1px solid rgba(170,220,255,.28); border-radius:16px; backdrop-filter: blur(8px); box-shadow...; pointer-events:auto; user-select:none;}
button...
.status...
.hint...
</style>
</head>
<body>
<canvas id="scene"></canvas>
<div class="panel" id="hud">
<h1>Glass Aquarium Failure</h1>
<div class=hint>Drag the glowing crack on the right wall. Lower cracks create more pressure. Trigger burst with button or Space.</div>
<div class=bar><span>Waterline</span><div><i id=waterFill></i></div><b id=waterPct>88%</b></div>
<div class=row><span>Flow</span><b id=flowPct>0%</b></div>
<div class=row><span>Crack depth</span><b id=depthText>...</b></div>
<div class=buttons><button id=burst>Trigger burst</button><button id=reset>Reset</button></div>
<div class=legend>...</div>
</div>
<script>
(() => {
'use strict';

const canvas = document.getElementById('scene');
const ctx = canvas.getContext('2d');
let dpr=1, W=0,H=0;
function resize(){ dpr=Math.max(1, Math.min(2, window.devicePixelRatio||1)); W=window.innerWidth; H=window.innerHeight; canvas.width=Math.floor(W*dpr); canvas.height=Math.floor(H*dpr); canvas.style.width=W+'px'; canvas.style.height=H+'px'; ctx.setTransform(dpr,0,0,dpr,0,0); setupAquarium(); }
But setupAquarium resets? Need not reset on resize? If resize, adjust positions maybe. Could preserve relative. Simpler: call setupAquarium resets simulation each resize? Maybe not desirable. But okay? Better preserve: compute aquarium and adjust crackY proportionally. But if user dragging maybe. We'll implement function resize with if initial then setup else adjust.
Need define state before resize. Let's define `const state = {...}; let aquarium={};`.
function setupAquarium() {
const margin=26; const floorPad=34;
let ax=margin; let ay=74;
let aw=Math.min(820, W-margin*2-130);
if (W<760) aw=Math.max(420, W-margin*2-70);
let ah=Math.min(520, H-220);
if (H<600) ah=Math.min(420, H-210);
let floorY=Math.min(H-floorPad, ay+ah+32);
if (floorY < ay+ah+22) floorY=ay+ah+22;
ah=floorY-ay;
let ax2 = margin;
let rightWallX=W-margin;
let bottomY=ay+ah;
aquarium = {x:ax, y:ay, w:aw, h:ah, bottom:bottomY, right:ax+aw, top:ay, floorY, leftWall:margin, rightWall:W-margin};
waterline = Math.min(bottomY-50, ay+ah*0.88);
if (!initialized) crackY=bottomY-ah*0.58 else crackY = clamp(crackY/oldH?); Need maybe.
}
But `setupAquarium` uses old. Let's simpler:
let aquarium = {};
function computeLayout() { ... }
function resize() {
const old = aquarium;
computeLayout();
if (!old.w) { crackY = clamp(crackY,...); } else { const ratio=(crackY-old.bottom)/(old.h||1); crackY=clamp(old.bottom+ratio*aquarium.h, ...); waterline = clamp(waterline/old.h*newh,...); }
}
Need ensure if object y positions not adjusted. Could be okay. Or reset on resize? Maybe not.

Initial state:
const state = {
burst: false,
triggered: false,
time:0,
crackY: 0,
crackLength: 64,
crackDragging:false,
objects:[],
particles:[],
shards:[],
puddle: {left:0,right:0,depth:0},
last: performance.now(),
waterline:0,
outflow:0,
flowStrength:0,
pressure:0,
opening:0,
shardTimer:0
};

Need call resetSimulation() after layout. But resize may call setup. Sequence:
computeLayout(); resetSimulation(); resize event calls computeLayout(); preserve state maybe; if not reset.
Maybe simpler:
function resize() {
const old = aquarium;
computeLayout();
if (!old.w) {
state.waterline = aquarium.y + aquarium.h*0.88;
state.crackY = aquarium.bottom - aquarium.h*0.58;
} else {
state.waterline = clamp(state.waterline * (aquarium.h/old.h), aquarium.top+40, aquarium.bottom-20);
state.crackY = clamp(state.crackY * (aquarium.h/old.h), aquarium.top+20, aquarium.bottom-20);
}
}
window.addEventListener('resize', resize);
computeLayout(); resetSimulation();
Need resetSimulation sets state and uses aquarium.

Need `resetSimulation`:
state.burst=false; state.time=0; state.waterline=aquarium.y+aquarium.h*0.88; state.crackY=aquarium.bottom-aquarium.h*0.58; objects=[]; particles=[]; shards=[]; puddle...
create objects.

Object creation:
function createObjects() {
add fish at x random 120..600, y waterline+40..bottom-100, maybe 6.
rock...
plant...
toy...
}
Need if reset after resize, objects positions relative.

Need current and physics functions.

Detailed functions:

function getPressure() {
return Math.max(0, state.waterline - state.crackY);
}
function getOpening() {
const p=getPressure();
if (!state.burst) return Math.max(2, Math.min(14, 3 + Math.sqrt(p)*0.08)); // visible crack maybe
return 4 + Math.min(36, Math.sqrt(Math.max(0,p))*0.38 + state.burstTime*8);
}
But state.burstTime increments only burst. Need opening not shrink. `state.burstTime += dt`.
Flow:
function updateHydraulics(dt) {
const p=Math.max(0,state.waterline-state.crackY);
const open=getOpening();
if (state.burst && p>0) {
const flow = open * Math.sqrt(p) * 1050; // px^3/s
const tankArea = aquarium.w * (state.waterline-aquarium.top);
state.outflow = flow;
state.flowStrength = clamp(flow / 260000,0,1);
const drop = flow / tankArea * dt;
state.waterline += drop;
if (state.waterline > aquarium.bottom - 18) { state.waterline=aquarium.bottom-18; state.outflow=0; state.flowStrength=0; }
// puddle
state.puddle.depth = clamp(state.puddle.depth + flow*dt/12000, 0, 100);
const spread = flow * dt / 16000; // px/s?
state.puddle.right = clamp(state.puddle.right + spread, state.puddle.left, aquarium.rightWall-10);
} else { state.outflow=0; state.flowStrength=0; }
}
Need if waterline at bottom, no flow.

Spawn:
function spawnWaterParticles(dt) {
if (!burst) return;
const p=...; if p<=0 return;
const open=getOpening();
const rate = 80 + state.flowStrength*520 + open*12;
state.spawnAccumulator += rate*dt;
while (acc >1) { acc--; spawn...}
}
Particle spawn:
const speed = 120 + Math.sqrt(p)*6 + state.flowStrength*180 + random(-20,60);
const vy = random(-80,80) - (state.waterline-state.crackY)*0.05;
x=aquarium.right+4; y=crackY+random(-open*0.45, open*0.45);
r=random(1.2,3.2)
life=4
If object maybe.

Update particles:
for i backwards:
p.vy += 880*dt;
p.vx *= Math.exp(-0.12*dt);
p.vy *= Math.exp(-0.05*dt);
p.x += p.vx*dt; p.y += p.vy*dt; p.life-=dt;
if (p.x > aquarium.rightWall-10 || p.y > aquarium.floorY - p.r) land:
p.y=aquarium.floorY-p.r; p.vx*=0.35; p.vy*= -? maybe absorb; p.life=0; state.puddle.depth += 1.2; state.puddle.right = clamp(...);
if p.y > aquarium.bottom && p.x between aquarium.x and aquarium.right? splash? But outside.
Need maybe if p.y > aquarium.floorY, remove.

Draw stream curve:
function drawJetCurve() {
if !burst return;
p=pressure; if p<=0 return; open; ctx.beginPath; for t=0..1.1 step .05:
x=right+4 + (180+sqrt(p)*6+flow*160)*t;
y=crackY + random? deterministic? Use no random for curve. vy = random? Use `Math.sin(t*5)*30`; y=crackY + (-30 + t*120)*t? Actually gravity: y=crackY + 20*t + 0.5*880*t^2; but visually. Stroke from t=0 to 0.7.
}

Objects update details:
function updateObjects(dt) {
for each obj:
if (obj.ejected) updateEjectedObject; else updateUnderwaterObject.
}
Current function:
function currentAt(x,y) {
const p=pressure; if (!burst || p<=0) return {x:0,y:0};
const dx=x-aquarium.right; const dist=Math.abs(dx);
const depthFactor = clamp((state.waterline-y)/(state.waterline-aquarium.top),0,1); // 1 near surface? maybe
const near = Math.exp(-dist/150);
const openingFactor = clamp((Math.abs(y-state.crackY))/(getOpening()*2.5),0,1);
const jet = Math.exp(-Math.abs(y-state.crackY)/Math.max(30, getOpening()*2));
const strength = p>0 ? (120 + Math.sqrt(p)*4 + state.flowStrength*260) : 0;
return {x: near*jet*strength*depthFactor, y: jet*strength*0.08};
}
Maybe current from breach to right: x positive. If x inside tank near right, pulls right. If x far, weak.

Update underwater:
const c=currentAt(obj.x,obj.y);
const r=obj.r || obj.size || 18;
const depthRatio = (obj.y - state.waterline)/(aquarium.bottom-state.waterline); // 0 top, 1 bottom
const dragExp = Math.exp(-obj.drag*dt);
obj.vx *= dragExp; obj.vy *= dragExp;
obj.vy += (1 - obj.density) * 360 * dt; // density<1 upward, >1 down.
obj.vx += c.x*dt; obj.vy += c.y*dt;
if fish:
obj.tail += dt*12; obj.vx += (-60 + Math.sin(obj.tail)*8) * dt; // swim left. But force in velocity units.
if (!state.burst && c.x < 30) obj.vx += Math.sin(...)*20? Maybe.
if (state.burst && c.x>200 && obj.x>aquarium.right-120) obj.vx += 300*dt;
// fish face?
rock:
if obj.y > aquarium.bottom-r-2: obj.y=bottom-r; obj.vy*=-0.25; obj.vx*=0.75;
plant:
obj.phase += dt*2; obj.vx += Math.sin(time*3+phase)*12*dt; obj.vy += (1-density)*600*dt; if bottom clamp.
toy:
obj.y = lerp(obj.y, state.waterline+obj.r*0.2 + Math.sin(time*3)*4, dt*1.5); // bob.
// general clamp:
obj.x=clamp(obj.x, aquarium.x+obj.r, aquarium.right-obj.r-4);
obj.y=clamp(obj.y, state.waterline+obj.r, aquarium.bottom-obj.r-2);
// eject if near breach:
if (state.burst && p>0 && obj.x > aquarium.right - 35 && Math.abs(obj.y-state.crackY) < getOpening()*1.25) { ejectObject(obj); }
Maybe if fish is far but current pushes, it moves right. Good.

Eject:
function ejectObject(obj) {
obj.ejected=true; obj.outX=aquarium.right+random(0,10); obj.outY=clamp(state.crackY+random(-state.opening*0.25, state.opening*0.25), aquarium.top+20, aquarium.bottom-20);
obj.vx=180+state.flowStrength*420+random(-40,80);
obj.vy=random(-90,80) - (state.waterline-state.crackY)*0.15; // maybe upward if high pressure?
obj.airDrag=obj.drag*0.4;
obj.onFloor=false;
obj.spinSeed...
}
Need if toy density low, maybe `obj.vx += 80; obj.vy -= 60`? But all ejected.

Update ejected:
const dragExp=Math.exp(-obj.airDrag*dt);
obj.vx*=dragExp; obj.vy*=Math.exp(-0.08*dt);
obj.vy += 880*dt;
obj.x += obj.vx*dt; obj.y += obj.vy*dt; obj.rot += obj.spin*dt;
if (obj.x < aquarium.leftWall+obj.r || obj.x > aquarium.rightWall-obj.r) { bounce; }
if (obj.y >= aquarium.floorY - obj.r) { obj.y=aquarium.floorY-obj.r; if (Math.abs(obj.vy)>80) obj.vy*=-0.18; else obj.vy=0; obj.vx*=0.72; obj.onFloor=true; }
if (obj.y < aquarium.top+obj.r) { obj.y=top+r; obj.vy=Math.abs(vy)*0.25; }
if (obj.onFloor && Math.abs(obj.vx)<2) obj.vx=0;
Maybe if object falls into puddle, x floor. Good.

Draw objects:
- if ejected, draw at outside. Else underwater.
Fish shape:
ctx.save translate(obj.x,obj.y) rotate maybe obj.angle; if ejected use angle based on velocity. Fish body ellipse, tail triangle, eye. If underwater, angle maybe -0.2? Actually fish swims left; body rotated -0.2.
Rocks: polygon irregular.
Plants: underwater, draw stem wavy line anchored bottom, leaves. If ejected, draw as wavy line.
Toy: circle/boat/duck? Draw yellow rubber duck floating. Underwater maybe bob. Ejected draw duck rotated.

Need "floating toy reacting differently according to density, buoyancy, drag, current": draw toy at surface, bobbing, current pushes. Good.

Draw aquarium:
Need draw background room:
function drawRoom() {
gradient dark walls; floor. Draw left/right wall lines, floor.
}
Draw aquarium:
ctx.save; roundRect maybe.
Draw back glass: rect with translucent fill.
Draw water: inside rect clip. Fill from waterline to bottom with gradient. Draw waves:
ctx.beginPath; for x ax to right step 8: y=waterline + sin((x*0.03)+time*2)*2; line. Fill below. Maybe draw water with alpha.
Draw air above.
Draw bubbles: Need maybe not required but nice. Generate bubbles? Could add bubble objects rising based on waterline. But not required. Could draw small bubbles.

Need maybe visible waterline lowers. Draw line at waterline with label.

Draw side glass and crack:
- Draw glass border with stroke white, blue highlights.
- Draw crack:
if !burst:
draw jagged vertical line from crackY - length/2 to + length/2. Use path with small horizontal offsets. Draw glow if dragging.
draw handle circle at crackY: cyan ring, maybe text "drag".
if burst:
draw missing opening: black/blue ellipse/irregular; draw broken edge fragments. Opening width=opening; y=crackY. Draw jagged outline. Draw shards.
Need if opening width > 0. `drawOpening` irregular path:
ctx.beginPath(); for k 0..16: angle? width = opening * (0.5+0.5sin). x=aquarium.right + width/2 * sin(angle); y=crackY + Math.sin(angle*2)*opening*0.15; line. Fill black/blue with alpha. Stroke white.
Need "side panel develops visible crack and then bursts." Good.

Draw water jet:
Before burst maybe no. After burst, draw particles and curve. Use ctx.globalAlpha.

Draw puddle:
floorY maybe aquarium.bottom+32. Puddle y=floorY+10? Draw ellipse:
ctx.save; ctx.beginPath(); ctx.ellipse((left+right)/2, floorY+12, (right-left)/2, 8+puddle.depth*0.06, 0,0,2pi); fill gradient? Canvas gradient linear x. Need maybe if left==right no. Draw semi transparent. Add highlights. Draw boundary collision maybe small red? Not necessary. Draw label? Maybe not.

Draw HUD update:
DOM ids. Need update each frame:
waterPct = Math.round((waterline - top)/h*100)
flowPct = Math.round(flowStrength*100)
depthText = pressure>0 ? `${Math.round(pressure/3.5)} cm equivalent` maybe.
waterFill width = pct.
burst button text maybe "Bursting..." disabled?
Need maybe pointer events.

Input:
canvas.addEventListener('pointerdown', e => {
const pt=getCanvasPoint(e);
if (isCrackHandle(pt.x,pt.y)) { state.crackDragging=true; canvas.setPointerCapture(e.pointerId); }
});
pointermove if dragging: crackY=clamp(y, top+20,bottom-20);
pointerup release.
Maybe if pointerdown not on handle and near crack? Also maybe drag from anywhere on right panel.
Trigger:
burstBtn.addEventListener('click', triggerBurst)
window.addEventListener('keydown', if Space not input)
resetBtn.

Need `getCanvasPoint` account CSS.

Need maybe if user drags crack vertical before triggering, lower crack stronger. Need maybe lower crack status. On trigger if crack above waterline? If user drags high above waterline, flow zero; but requirement lower crack stronger. We can allow. But if above waterline, no water. Maybe instruct. To ensure visible failure, maybe if no pressure, waterline still above? If crackY < waterline? Actually y smaller = higher. Lower crack means y larger. Need if user sets below waterline. We can show pressure. If above, no flow. Trigger burst but no jet; shards. But maybe "lower cracks create more pressure." Good.

Potential issue: if reset sets crackY bottom - h*0.58, waterline top+h*0.88, pressure = bottom - h*0.58 - (top+h*0.88)=h*0.12 = 62 px, moderate. Maybe if lower crack, stronger. Good.

Need maybe `state.burstTime` increments only after burst. Opening grows. But if waterline drops below crackY, flow zero but opening remains. Good.

Need maybe waterline lowering "continuously rather than disappearing all at once": update.

Need maybe objects ejected after burst; if flow low, maybe not. We can force some objects near breach ejected:
At trigger, maybe add `state.launchCooldown=1`. In update, if burst and current strong. But objects initially maybe not near breach. Need fish eventually swept through breach. We can ensure by current pulling objects right. But if current too weak, maybe no. Need add `if state.burst && pressure>0 && obj.x > aquarium.right - (160 + open*3) && Math.abs(obj.y-crackY) < max(open*1.8, 120)`, then eject. This could eject fish/toy/rock/plant near opening. But requirement fish attempts to swim against current before swept. We can include fish first then others. Need maybe only fish near right, toy. Objects initial x maybe fish at 650 near right, crack y 300, opening range maybe 100, so some ejected. Good.
But if crack high and current weak, maybe not. Good.

Need maybe object "airborne motion then floor collisions": If ejected, they land on floor. Good.

Need maybe rocks/plants can be ejected? Current pulls them; maybe not all. But requirement objects plural. Good.

Need maybe "collisions" for glass fragments with boundaries and floor. Good.

Need maybe "water resistance" for fragments: implement in updateShards. Need maybe water resistance for objects too.

Let's craft code. Need maybe ensure no syntax errors. Let's write.

Potential browser compatibility:
- `ctx.roundRect` maybe supported but can use helper `roundedRect`.
- `Path2D` no.
- `Math.clamp` not. Use custom.

Need use `ctx.ellipse` supported.

Need maybe `backdrop-filter` okay.

Let's write full index.html.

Need maybe include CSS `button:disabled`.

Let's code.

Important: `ctx.setTransform(dpr,0,0,dpr,0,0)` after resize; if `ctx` variable. Good.

Need maybe use `ResizeObserver`? no.

Detailed JS skeleton:

(() => {
const canvas...
const hud = { ... };
let W=0,H=0,dpr=1;
const aqua = {};
const state = {...};
function clamp(v,min,max){...}
function rand(min,max){...}
function lerp(a,b,t){...}
function easeOut(t){...}
function computeLayout(){
const margin=26;
let floorPad=34;
let aw=Math.min(820, W - margin*2 - 130);
if (W<760) aw=Math.max(430, W - margin*2 - 60);
let ah=Math.min(520, H - 210);
if (H<620) ah=Math.min(440, H - 210);
aw = Math.max(420, Math.min(aw, W - margin*2 - 40));
ah = Math.max(320, Math.min(ah, H - 190));
let floorY = Math.min(H - 28, aqua.y + ah + 34); // but aqua.y not set
let ay=72;
let bottom=ay+ah;
if (floorY < bottom+22) floorY = bottom+22;
if (floorY > H-20) floorY=H-20;
ah=floorY-ay;
aquarium = {x:margin, y:ay, w:aw, h:ah, bottom:ay+ah, right:margin+aw, top:ay, floorY, leftWall:margin, rightWall:W-margin};
}
Need if H small, floorY could > H. But okay.

`resetSimulation`:
if (!aquarium.w) computeLayout();
state.burst=false; state.triggered=false; state.time=0; state.opening=4; state.burstTime=0; state.outflow=0; state.flowStrength=0; state.waterline=aquarium.y+aquarium.h*0.88; state.crackY=aquarium.bottom-aquarium.h*0.58; state.particles=[]; state.shards=[]; state.puddle={left:aquarium.right+8,right:aquarium.right+8,depth:0}; state.spawnAcc=0; state.shardTimer=0;
createObjects();
updateHud();
}

`createObjects`:
state.objects = [
{type:'fish', x:aqua.x+aqua.w*0.22, y: waterline+80, vx:-35, vy:20, r:18, density:1.0, drag:.75, color:'#ff8a4c', tail:0},
...
];
Need maybe 5 fish. Use random.
Rocks:
{type:'rock', x:..., y:aqua.bottom-80, r:22, density:2.4, drag:.25, rot:0, spin:0.5}
Plants:
{type:'plant', x:..., y:aqua.bottom-10, r:24, density:.75, drag:1.1, stem:100, phase:random}
Toy:
{type:'toy', x:aqua.x+aqua.w*0.75, y:waterline+25, r:24, density:.45, drag:.45, rot:0}
Maybe create 4 fish. Good.

Need `updateHud` uses DOM. Could update every frame.

Physics:
`update(dt)`:
state.time += dt;
computeOpening maybe.
updateHydraulics(dt);
spawnWaterParticles(dt);
updateParticles(dt);
updateObjects(dt);
updateShards(dt);
updateHud();

Need order: If waterline update before spawn, okay. Current uses new pressure. Maybe spawn after update. Objects ejected based on pressure. Fine.

`updateHydraulics`:
const p = Math.max(0, state.waterline - state.crackY);
const open = getOpening();
state.opening = open;
if (state.burst && p>0) {
const tankHeight = Math.max(1, state.waterline - aquarium.top);
const flow = open * Math.sqrt(p) * 1100;
state.outflow = flow;
state.flowStrength = clamp(flow / 300000, 0, 1);
state.waterline += (flow / (aquarium.w * tankHeight)) * dt;
if (state.waterline > aquarium.bottom - 12) { state.waterline=aquarium.bottom-12; state.outflow=0; state.flowStrength=0; }
state.puddle.depth = clamp(state.puddle.depth + flow*dt/14000,0,120);
const spread = flow*dt/18000;
state.puddle.right = clamp(state.puddle.right + spread, state.puddle.left, aquarium.rightWall-12);
} else { state.outflow=0; state.flowStrength=0; }
Need if waterline lower, tankHeight maybe small. Good.

`getOpening`:
const p=...
if (!state.burst) return 5 + Math.min(18, Math.sqrt(p)*0.12);
return 3 + Math.min(44, Math.sqrt(p)*0.34 + state.burstTime*5.5 + state.flowStrength*12);
Need if p=0 but burst, opening maybe 3+burstTime*5.5? That's okay.

`updateParticles`:
for i backwards:
p.vy += 880*dt; p.vx *= Math.exp(-0.10*dt); p.vy *= Math.exp(-0.05*dt);
p.x += p.vx*dt; p.y += p.vy*dt; p.life -= dt;
if p.x > aquarium.rightWall-12 || p.y > aquarium.floorY-p.r:
p.y=...; p.vx*=0.2; p.vy*=-0.1; p.life=0; state.puddle.depth=clamp(...); state.puddle.right=clamp(...);
if p.y > aquarium.bottom && p.x>=aqua.x && p.x<=aqua.right: splash maybe remove; no.
Maybe if p.y > aquarium.bottom and x inside tank, spawn splash and remove. But particles outside.

`spawnWaterParticles`:
if !burst return;
const p=pressure; if p<=0 return;
const rate=100+state.flowStrength*620+state.opening*18;
state.spawnAcc += rate*dt;
while state.spawnAcc >=1: state.spawnAcc--; spawn...
Need if p<=0 but burst, no.

`currentAt`:
const p = pressure; if (!state.burst || p<=0) return {x:0,y:0};
const open = state.opening;
const dist = aquarium.right - x; // positive inside
const near = clamp(dist/260,0,1); // 1 if within 260, else 0. But `exp(-dist/180)`.
const depthFactor = clamp((state.waterline-y)/(state.waterline-aquarium.top),0,1);
const vertical = Math.exp(-Math.abs(y-state.crackY)/Math.max(24,open*2.2));
const strength = 100 + Math.sqrt(p)*5 + state.flowStrength*300;
return {x: Math.exp(-dist/180)*vertical*strength*depthFactor, y: vertical*strength*0.08};
Maybe if x far left current low.

`updateObjects`:
const p=pressure; const open=state.opening;
for obj:
if ejected...
else:
const c=currentAt...
const drag=Math.exp(-obj.drag*dt);
obj.vx *= drag; obj.vy *= drag;
obj.vy += (1 - obj.density)*420*dt;
obj.vx += c.x*dt; obj.vy += c.y*dt;
if type fish:
obj.tail += dt*(8+obj.speed);
obj.vx += (-obj.swim + Math.sin(obj.tail)*12) * dt;
obj.vy += Math.sin(obj.tail*1.7)*8*dt;
if (state.burst && p>0 && c.x>120 && obj.x > aquarium.right - 140) obj.vx += (c.x-120)*0.5*dt;
if type rock:
obj.rot += obj.spin*dt;
if obj.y > aquarium.bottom-obj.r-2 { ... }
if type plant:
obj.phase += dt*2.2;
obj.vx += Math.sin(state.time*2.8+obj.phase)*10*dt;
obj.vy += (1-obj.density)*720*dt;
if obj.y > aquarium.bottom-obj.stem/2-obj.r { ... maybe anchor; }
if type toy:
obj.phase += dt*4;
const targetY = state.waterline + obj.r*0.18 + Math.sin(state.time*4)*3;
obj.vx += (targetY - obj.y)*0.8*dt; // if below, upward? Actually if y>target, move up.
obj.vy += (targetY - obj.y)*1.8*dt;
obj.vy += (1-density)*450*dt;
obj.vx += c.x*0.25*dt;
// clamp
obj.x=clamp(obj.x,aqua.x+obj.r,aqua.right-obj.r-2);
obj.y=clamp(obj.y,state.waterline+obj.r,aqua.bottom-obj.r-2);
if (state.burst && p>0 && obj.x > aquarium.right - 45 && Math.abs(obj.y-state.crackY)<open*1.35) ejectObject(obj);
// For fish if x pushed near breach but not in opening, maybe if open range larger:
if (state.burst && p>0 && obj.x > aquarium.right - 100 && Math.abs(obj.y-state.crackY)<Math.max(open*2.0, 90)) ejectObject(obj);
But this may eject all fish near right. Good.
Need fish "attempting to swim against current before being swept through breach": fish vx negative; current c.x positive. Good.

Potential issue object `obj.r` for plant maybe stem. For plant clamp bottom. Fine.

`ejectObject(obj)`:
obj.ejected=true; obj.onFloor=false; obj.outY=clamp(state.crackY + rand(-state.opening*0.35, state.opening*0.35), aquarium.top+15, aquarium.bottom-15); obj.outX=aquarium.right+rand(0,12); obj.vx=180+state.flowStrength*420+rand(-40,80); obj.vy=rand(-90,70) - Math.min(120,state.flowStrength*180); obj.airDrag=obj.drag*0.35; obj.spinSeed=rand(0,Math.PI*2); obj.spin=rand(-5,5); obj.onFloor=false;
Need if toy maybe buoyant but ejected: maybe `obj.vy -= 40`.
Maybe if rock, `obj.vx += 100`.

Update ejected:
const drag = Math.exp(-obj.airDrag*dt);
obj.vx *= drag; obj.vy *= Math.exp(-0.05*dt);
obj.vy += 880*dt;
obj.x += obj.vx*dt; obj.y += obj.vy*dt; obj.rot += (obj.spin || 0)*dt;
if (obj.x < aquarium.leftWall+obj.r || obj.x > aquarium.rightWall-obj.r) { obj.x=...; obj.vx*=-0.28; obj.vy*=0.88; obj.spin*=0.9; }
if (obj.y >= aquarium.floorY - obj.r) { obj.y=...; if (Math.abs(obj.vy)>90) obj.vy*=-0.18; else obj.vy=0; obj.vx*=0.72; obj.onFloor=true; obj.spin*=0.95; }
if (obj.y < aquarium.top+obj.r) { obj.y=...; obj.vy=Math.abs(obj.vy)*0.35; }
if (obj.onFloor && Math.abs(obj.vx)<2) obj.vx=0;
Need if x > rightWall and y floor, clamp. Good.

Shards:
createShards:
const n=100; for i:
y=crackY+rand(-crackLength/2, crackLength/2); x=aqua.right+rand(-2,2);
speed=80+rand(0,320); angle=rand(-Math.PI*0.75,Math.PI*0.75); vx=cos*speed; vy=sin*speed; size=rand(6,18); rot=rand; spin=rand(-12,12); shape=...
state.shards.push.
Update shards:
for i backwards:
const insideWater = state.burst && s.x>=aqua.x && s.x<=aqua.right && s.y>=state.waterline && s.y<=aqua.bottom;
const res = insideWater ? 3.4 : 0.28;
s.vx *= Math.exp(-res*dt); s.vy *= Math.exp(-(insideWater?2.2:0.12)*dt);
s.vy += 880*dt;
s.x += s.vx*dt; s.y += s.vy*dt; s.rot += s.spin*dt;
if (s.y > floorY - s.size) { s.y=floorY-s.size; s.vy*=-0.12; s.vx*=0.72; s.spin*=0.85; if(abs(s.vy)<18) s.vy=0; }
if (s.x < leftWall+s.size || s.x > rightWall-s.size) { s.x=...; s.vx*=-0.32; s.vy*=0.85; s.spin*=0.9; }
if (s.y < top+s.size) { s.y=top+s.size; s.vy=Math.abs(s.vy)*0.25; }
if (s.y > bottom && s.x>=aqua.x && s.x<=aqua.right) { s.y=bottom; s.vy*=-0.05; s.vx*=0.6; }
if (s.life -=dt <=0) remove? Need shards have life? We'll keep maybe 20s. Add life.
Need if waterline lower, insideWater maybe less. Good.

Drawing:
`draw()`:
ctx.clearRect(0,0,W,H);
drawRoom();
drawPuddleBack? maybe before aquarium? Puddle floor should be after floor. Draw aquarium. Draw jet maybe after aquarium? Water jet outside should overlay. Draw objects maybe inside tank and ejected. Need order:
1 background room
2 aquarium glass/water/crack
3 objects underwater (inside)
4 water jet/particles outside
5 shards outside and maybe inside
6 puddle on floor (after objects? Puddle should be on floor; objects landing on puddle maybe after? Draw puddle before falling objects? maybe draw puddle after aquarium but before objects? Ejected objects on floor should appear on puddle. Draw puddle before objects? If object on floor, draw puddle first then objects. But puddle on floor should cover floor, not tank. Draw floor, puddle, aquarium, objects? Aquarium bottom covers floor. Puddle starts outside right. Draw room floor and puddle first, then aquarium. Then objects (both inside and ejected), then shards, then HUD? HUD DOM.
Maybe draw order:
drawRoom
drawPuddle
drawAquarium (water/glass/crack)
drawObjects (underwater and ejected)
drawWaterJet
drawParticles
drawShards
drawForegroundHighlights
Need water particles should appear outside over glass; draw after aquarium. Good.
But water particles inside? none.
Draw aquarium water before objects. Good.

`drawRoom`:
gradient sky/dark. floor rect y=floorY to H. Draw floor line. Walls at left/right. Maybe subtle grid.

`drawPuddle`:
if right>left:
const left=puddle.left, right=puddle.right, y=floorY+14, h=clamp(6+depth*0.06,6,20);
gradient = ctx.createLinearGradient(left,0,right,0); add color stops.
ctx.beginPath(); ctx.ellipse((left+right)/2,y,(right-left)/2,h,0,0,2pi); fill.
Draw top line maybe.
If reaches right wall, draw vertical splash/wall wet mark? Could draw blue stain on right wall from floorY to floorY+puddleDepth*0.1. Good.

`drawAquarium`:
- clip to rect.
- draw air background inside: gradient light blue.
- draw water: clip to rect. Fill water from waterline to bottom. Need gradient vertical.
ctx.fillStyle waterGrad; ctx.fillRect(ax,waterline,aw,bottom-waterline).
Draw water surface wave:
ctx.beginPath(); for x ax to right step 8; y=waterline+sin(x*0.04+time*2)*2; line; stroke rgba.
Draw depth shading.
Maybe draw bubbles array? Could generate dynamic bubbles:
In draw, draw small bubbles with deterministic:
for i=0..30: x=aqua.x+10+(i*73 % (aqua.w-20)); y=aqua.bottom - ((state.time*35 + i*37) % (aquarium.bottom-state.waterline+20)); alpha=...
But if waterline changes, okay.
- draw glass border after clip.
- draw crack/opening on right side after water maybe. Need crack line on glass outside. Draw before/after water? Crack on glass. Draw after border.
`drawCrackAndOpening(open)`:
if !burst:
path jagged vertical. Stroke dark with glow. Handle.
else:
draw opening irregular filled dark with blue. Then jagged edge.
Need maybe draw "bursted" with missing side panel.

`drawObjects`:
Loop objects. If !ejected drawUnderwaterObject; else drawAirborneObject. For underwater, maybe draw inside tank. For ejected, draw outside. Need if toy floats at waterline.

Shapes:
Fish:
function drawFish(obj, air) {
ctx.save(); translate; if air rot=atan2(vy,vx), else rot=-0.25? Actually fish swims left, face left, so rotate -Math.PI/2? Hmm coordinate x right, y down. Body ellipse elongated x; if swimming left, nose points left, rotate Math.PI. But if rotate Math.PI, eye at front left? Easier draw body centered with tail at -r, head at +r? Fish swims left, head should left. Draw tail at -r, head at +r, rotate Math.PI? If rotate 0, head right. So for fish swimming left, rotate Math.PI. But if ejected, angle = atan2(vy,vx) maybe. For underwater: angle = Math.PI + Math.sin(tail)*0.1.
ctx.rotate(angle);
tail triangle at -r; body ellipse at 0,0 width 2r height r; head at +r; eye at +r*0.55 maybe.
}
Rocks:
draw irregular polygon.
Plants:
if underwater: anchor at bottom, stem to y-bottom-stem; wavy. If ejected, anchor at obj.y? We'll draw wavy stem with leaves.
Toy:
Draw duck: body ellipse, head circle, beak. If underwater, maybe still.
Rocks: polygon.

Need maybe draw current arrows near breach:
`drawCurrentArrows`: if burst and pressure>0, draw few arrows from right wall into water? Actually current pushes right; draw arrows inside tank near right pointing right, with alpha. Good.

`drawWaterJet`:
if burst:
draw deterministic stream curve:
ctx.save; ctx.globalAlpha=0.2+0.45*flow; ctx.strokeStyle='rgba(120,220,255,.75)'; ctx.lineWidth=2+open*0.08;
begin; for t=0..0.85 step .05:
x = right+4 + (180+sqrt(p)*5+flow*140)*t;
y = crackY + (-20 + t*80)*t + 0.5*880*t*t;
lineTo.
stroke.
Maybe draw splash at floor:
if puddle depth >0 draw ripples.

`drawParticles`:
for p of particles: alpha=clamp(p.life/0.5,0,1); fill small blue circles.

`drawShards`:
for s:
ctx.save translate rotate; draw polygon shape. Shape maybe generated with points. Fill rgba(180,240,255,0.85), stroke rgba(255,255,255,.8).
Need `s.points` maybe. Create:
const sides=4+floor(rand(0,3)); const pts=[]; for j: angle=2pi*j/sides+rand(-0.2,0.2); radius=s.size*rand(.6,1.2); pts.push({x:cos*radius,y:sin*radius});
Maybe not too many. Good.

`drawForegroundHighlights`:
glass shine lines.

Need maybe `drawWaterlineLabel`:
ctx.fillText(`waterline ${pct}%`, x=...). Good.

Need maybe UI water fill.

Potential issue: if canvas pointer events, HUD overlay blocks. Good.

Need maybe if user drags crack, lower crack should initially produce stronger jet. We can show "Pressure: ..."

Let's write file.

Need maybe after writing, run syntax check. Since script embedded, can extract with node? Use `node --check <(sed ...)` maybe not. Easier `node --check` can't parse HTML. We can extract script via `python` or bash. But first write.
write {}
assistant
thinking
We need answer by creating index.html in /tmp/ws. Need build single-page web app. Need inspect directory maybe. Need produce file. Need likely use Canvas 2D simulation. Need no external resources. Need implement requested features. Need ensure index.html present. We can write comprehensive HTML. Need maybe test. We can create a large glass aquarium side panel crack then burst simulation. Need drag crack vertically before triggering. Use canvas, CSS, JS inline. Need physics: water escaping through opening based on water depth decreasing as tank drains; curved water jet gravity; puddle collides room boundaries; fish rocks plants toy reacting density buoyancy drag current; objects underwater to airborne to floor collisions; fish swim against current before swept through breach; glass fragments angular velocity collisions and water resistance; waterline lowers continuously.

We need implement. Need maybe code robust.

Let's think design.

Single HTML canvas full viewport. Aquarium rectangle. Room boundaries: floor, walls. Side panel is right side? Large aquarium with side panel develops crack and bursts. User drag crack vertically before triggering failure. Lower crack stronger jet. Crack vertical draggable. Trigger button. On trigger crack grows/bursts, opening width increases, water jet from breach.

Coordinate system: canvas. Aquarium x=50, y=80, w=700, h=520 maybe. Room boundaries: floor y=canvas.height-70, left wall x=20, right wall x=canvas.width-20, maybe back? Aquarium on floor. Side panel is right wall of tank at x=750? If room boundaries are around tank and floor. Crack on right side panel. Opening at crack y. Jet exits to right and downward/up? Water escaping through opening, gravity curves downward. Puddle on floor at tank base to right, collides with room boundaries (right wall, maybe floor). Opening lower initially stronger due to water depth h = waterlineY - crackY (if crack below waterline). If higher crack less. If crack above waterline, leak small/none.

Need simulate water volume and waterline. Tank water volume decreases based on orifice area and velocity. Use orifice equation: pressure = rho*g*depthBelowSurface; flowRate = area * sqrt(2*g*depth) * discharge coefficient. If crack opening height maybe crack grows. Jet aperture height = crackLength? Opening width? We can set crack length grows from 0 to 80 px after trigger. Jet width = crackLength*0.55 maybe. Flow rate = area * sqrt(2g*depth) where area = openingWidth? In 2D side view, volume cross-section? We can approximate. Need lower crack stronger. Depth = waterlineY - crackY. Jet exit speed = sqrt(2*g*depth). Position (tankRight+maybe 10, crackY). x velocity maybe 220 + 200*sqrt(depth) maybe; y velocity small negative/up? Gravity downward. In canvas coordinates y positive down; exit y velocity maybe -20? Jet particles: spawn at crack with vx = 260 + 380*sqrt(depth) maybe, vy = -30 + random. Update under gravity. Particle collisions with floor, right wall, tank bottom, maybe bounce/damp. Puddle: Could represent puddle as dynamic particles or a deformable ellipse/shape. Need "spreading puddle collides with room boundaries". Simpler: maintain puddle particles on floor: each has x,y radius, vx,vy, age, wetness. Spawn particles from jet hitting floor. Puddle spreads horizontally along floor; collides with left/right walls and tank bottom, evaporates. Also use a "puddle mask" maybe to show water on floor as blue ellipse/shape with multiple lobes. Need robust.

Fish, rocks, plants, floating toy: objects in tank with properties density, buoyancy, drag, current. Need react differently. Fish attempt to swim against current before swept through breach. Need object states: underwater, airborne, floor collision. Underwater objects affected by buoyancy, drag, current; fish swims with goal? Need implement.

Objects:
- fish: density slightly less than water? swim speed, target avoid current; when near leak, swim against current (to left/up maybe) until overwhelmed; if exits breach, airborne with lower drag, gravity; floor collision.
- rocks: density > water, sink to tank bottom, buoyancy small, drag.
- plants: anchored to bottom, flexible, drift with current, maybe not move.
- toy: density low, floats near surface, bobbing, buoyant, dragged by current.

Need objects transition underwater->airborne->floor collisions. We can implement generic object physics:
Each object has x,y,vx,vy,r, mass, density, type. In tank water if y + r < waterlineY? Actually waterlineY is y coordinate surface, water region y > waterlineY. Underwater if center below surface. Airborne if above waterline or outside tank/leak. Buoyancy: if underwater, effective weight = mass*g*(density - waterDensity). For density less than water (toy) buoyant upward; fish near neutral maybe density 0.98, rocks 2.5, plants maybe anchored. Drag: underwater high, airborne low. Current field in tank due to leak: velocity to right near leak, decreases with distance, maybe swirl. Fish has swim force: fish tries swim against current (left) if near leak and not too damaged; else normal swim. If current overwhelms, swept to breach. Once fish crosses tankRight (x > tankRight), state airborne. Airborne affected gravity, low drag. Collide floor (room floor) with restitution; if hits right wall, etc. Underwater floor collision tank bottom.

Plants anchored: maybe not transition; but can react. Need include.

Glass fragments: after burst, fragments spawn along crack, angular velocity, collisions and water resistance. Fragments are triangular polygons with x,y,vx,vy,rot,omega, density, radius, inside/outside. Some fragments initially inside tank, underwater; water resistance damps; gravity; collide with tank bottom, left wall, right wall/room? Need collisions. They can also collide with each other? Requirement collisions plural. Implement fragment-fragment simple circle collision with restitution maybe. Glass fragments with angular velocity, collisions, water resistance. Need draw angular.

Crack drag: before failure, user can drag crack along side panel vertically. Show crack dashed. Lower crack stronger jet. Need trigger button. Maybe start with crack at 300 px. Need waterline. Lower crack means closer to bottom, more water depth, stronger. User can drag up/down within tank. On trigger, crack grows. Maybe after trigger crack can't drag.

Need waterline lowers continuously. We can draw water surface line with gradient. Need update waterline based on water volume. In side view, tank cross-section water area = tankW * waterDepth. Water volume decreases by flowRate*dt; waterlineY = bottomY - waterDepth. FlowRate based on waterline and crack. Need account opening area grows. Need use units px. Let g maybe 900 px/s^2. Pressure depth = max(0, waterlineY - crackY). Exit velocity = sqrt(2*g*depth). Flow rate px^2/s = k * crackLength * sqrt(depth) maybe. waterDepth -= flowRate*dt / tankW. If crack below bottom etc. Flow decreases as tank drains. Need lower crack stronger: at initial waterlineY 120, bottom 580. crack at 350 => depth 230; crack at 180 => depth 410, stronger. Opening width/height maybe crackLength grows. If crack near top (depth small), weak.

Need visible waterline lower continuously. Draw line at y=waterlineY, moving downward (larger y) as water drains. Need maybe water depth label.

Need side panel burst: draw glass cracks, fragments, opening. Need maybe glass panel transparent.

Need room boundaries: floor at canvas bottom, side walls maybe left/right. Puddle collides with room boundaries: left wall x=40, right wall x=canvas.width-40. Aquarium on floor, right side panel near right wall? If right wall is close, jet hits it. Need room boundaries visible. Aquarium right wall maybe x=690, room right wall x=940. Puddle spreads to right wall. Good.

Let's set canvas responsive? Use fixed virtual dimensions 1000x680. CSS canvas width 100%, height 100%; JS scale. We'll use canvas internal 1000x680.

Layout:
const W=1000,H=680
Tank: {x:60,y:75,w:620,h:500} => right=680, bottom=575. Room floor y=620. left wall x=35, right wall x=965, maybe back wall x=25? Aquarium sits on floor.

Waterline initial maybe tank.y+80=155. Water depth = bottom-waterline=420. Crack initial y=350 (lower) so depth 195. Lower crack stronger. Need crack draggable vertically between tank.y+50 and tank.bottom-80.

Crack: {x:tank.right, y, length:0 (or 18 pre?), maxLen:90, active:false, triggered:false}. Pre-burst crack line on side panel; user drag. Trigger button. On trigger crack active, crack.length grows over time to maxLen? But opening should be visible. Maybe crackLength = ease(t) * maxLen; openingHeight = crackLength*0.75; openingWidth = 18+crackLength*0.25? In 2D, opening height vertical along side panel. Jet emerges through vertical slit. Flow rate = openingHeight * orificeWidth * sqrt(2g*depth). OpeningWidth maybe 8 + 0.2*crackLength. Need area.

After trigger, side panel cracks and fragments. Burst: tankRight panel maybe destroyed over time? But water can continue through opening at x=tank.right+5. Draw a jagged hole at crack.

Jet particles: spawn at x=tank.right + 8, y=crack.y + random(-openingHeight/2, openingHeight/2). vx = 220 + 520*sqrt(depth)/sqrt(maxDepth) maybe, vy = random(-40,40). Need if depth small, weak. If depth < 5 no particles. Update:
p.x += vx*dt; p.y += vy*dt; p.vy += gravity*dt; p.vx *= (1 - 0.05*dt) maybe; collisions: if p.y > floorY - p.r, p.y=floorY-p.r; p.vy*=-0.25; p.vx*=0.7; p.age+=dt; add to puddle particles. If p.x > roomRight - p.r, p.x=roomRight-p.r; p.vx*=-0.2; if p.y > floorY maybe. If p.x < tank.right? no.

Puddle particles: floor water droplets. x,y=floorY - r. vx maybe from impact. They spread along floor; friction; bounce small; collision with left wall x=roomLeft+r and right wall x=roomRight-r; floor. Age, radius. Draw as blue blobs. Could accumulate? Need puddle spreading. Spawn droplet particles. Also maybe draw puddle as "wet" ellipses with low alpha. Could use particles enough.

Flow from leak: Need spawn particles proportional to flowRate. Flow rate px^2/s; particle mass? Let spawnCount = flowRate*dt / 18? If flowRate 1000, dt 0.016 => 0.8 particles. okay.

Waterline update: waterDepth -= flowRate*dt / tank.w; clamp [0, bottom - top]. If waterline reaches crack? Actually if waterline below crack, no flow. Flow rate uses max(0, waterlineY - crackY) but if waterline lower than crack no leak. Since opening at side panel, if waterline above opening. Good. Need lower crack initially stronger.

Maybe also draw water with alpha.

Object physics:
Need design.

Tank interior. Objects:
Fish: x=200,y=300,vx=-30,vy=20, r=18, density=0.98, alive true, state 'underwater'. size. It should swim against current near leak. Current near leak: currentAt(x,y):
if y > waterlineY and x < tank.right:
let d = tank.right - x; let dy = Math.abs(y-crackY); let pressure = clamp((waterlineY - crackY)/ (bottom-waterlineY),0,1); let range = 260; let influence = exp(-(d*d)/(2*range^2)) * exp(-(dy*dy)/(2*range^2)) * pressure.
return {vx: influence*(280+300*sqrt(depth/maxDepth)), vy: -influence*30 maybe}
Also current below waterline. For fish, if state underwater and near leak (x>450 or |y-crackY|<80), fish attempts to swim against current: desired vx = -100 - influence*120, vy = -20 (toward surface? maybe). It "attempts" until current overwhelms. If influence > 0.5, net vx maybe positive; if x > tank.right-20 or fish's current pushes through, state airborne. But need fish being swept through breach. We can set if x > tank.right - 5 and influence > 0.25, state='airborne'. Need allow user trigger. For fish, when airborne, x may > tank.right, gravity, low drag, collisions with floor. Need draw.

Rock: x=160,y=480,vx=0,vy=0,r=22,density=2.6,state underwater. Buoyancy: effective acceleration downward = g*(density-waterDensity)/density? Actually net = g*(1 - waterDensity/density), downward if density>water. Underwater drag high. It sits on tank bottom. When waterline lowers below? If waterline rises above object? Waterline lowers, not high. It stays underwater until maybe tank drains? If waterline below rock? impossible if waterline lower than bottom? If waterline below top, still water around rock maybe. If waterlineY > rock.y+rock.r, object above waterline? As tank drains, if waterline lower (down), water depth decreases; objects near surface can become airborne. Rocks at bottom stay underwater until waterline below bottom? Since waterline lower means less water but still covers bottom until empty. If tank drains to empty, waterlineY=bottom+? object becomes airborne? Need handle. Underwater if center y > waterlineY and y+? Actually if waterlineY is above object's center, object is underwater. If waterlineY > object.y (surface below object center), underwater. If waterlineY <= object.y, object is above waterline, airborne. As waterline lowers downward, water surface descends, eventually objects near surface become airborne. Rocks at bottom remain underwater until waterline reaches bottom. Fine.

Toy: density=0.35, r=20, floats at surface. Underwater buoyancy upward. It should bob. Drag.

Plant: anchored at bottom; can bend with current. We'll draw plant stems as polylines with sway based on current; not physics object. Maybe include one plant "plant" object with anchor and segments, current.

Need object transitions:
For each object:
- Determine inWater = y + maybe bottom? Actually if object center below waterline and inside tank. If y > waterlineY? Underwater. If object above waterline, airborne.
- Forces:
Gravity always downward.
If inWater: buoyancy upward proportional (1 - density/waterDensity) * g. Drag coefficient waterDrag. Current adds drag to current velocity: object.v += (current - object.v)*waterDrag*dt.
If airborne: waterDrag low/0; drag coeff 0.15.
Fish swim: if inWater and not swept: add swim acceleration. If near leak, swim against current. If state airborne, no swim.
For fish, if inWater and x > tank.right-15 and current influence > 0.15, state airborne; maybe vy += current.vy.
For toy, if inWater and y < waterlineY+10, buoyancy pushes it to surface; if waterline lowers below toy, maybe toy becomes airborne then falls? Toy density low; if waterline drops below toy, it might be above water, but toy would float at surface. If tank drains, toy may be carried by current and through breach? But toy density low; current can push it. Need maybe toy gets swept through breach too if current strong. But if density low, buoyancy keeps it near surface; it can be carried through breach. We can allow if x > tank.right - 15 and current influence >0.35, state airborne. But floating toy should react differently, maybe more surface, less sinking.
Rocks sink and stay bottom.
Plants anchored and sway; not transition.
- Collisions:
Underwater tank walls/bottom: if x-r<tank.x+10, bounce; if x+r>tank.right-10, if not through? For objects except fish/toy maybe bounce; if fish swept can exit. If y-r<waterline? Actually water surface not solid. if y+r>bottom, y=bottom-r, vy*=-0.2.
Airborne room boundaries: floor y=roomFloor, walls x=roomLeft/roomRight. If x-r<roomLeft+r, bounce; if x+r>roomRight-r, bounce. If y+r>roomFloor, y=roomFloor-r, vy*=-0.25, vx*=0.85. If object falls outside? maybe.

Need maybe draw objects underwater and airborne differently. Fish drawn with tail animation; rocks polygon; plants; toy.

Glass fragments:
Spawn after trigger along crack: maybe 80 fragments. Each:
type glass, x=tank.right+random(-10,10), y=crackY+random(-crackLen/2, crackLen/2), vx=random(-80,220), vy=random(-160,80), rot, omega, angle points, size, mass, state maybe airborne/underwater if y>waterline. Fragment polygon 3-5 points.
Physics:
gravity, drag depending inWater (waterResistance high). If inWater, vx *= exp(-waterDrag*dt), vy *= exp(-waterDrag*dt), buoyancy maybe if glass density > water, downward. Collisions:
- tank bottom, left wall, right wall/room? Some fragments inside tank bounce off tank walls and bottom; outside bounce room boundaries.
- fragment-fragment collisions: simple circle collision with restitution; update velocities.
- water resistance: if inWater, damping 2.0, buoyancy.
- Angular velocity affected by collisions/drag.
Need draw fragments as polygon with rotation, maybe color #d7f6ff with alpha.

Need collision between fragments and maybe objects? Could include glass fragments colliding with objects? Requirement fragments collisions. We can implement fragment-fragment and boundary. Maybe also fragments bounce off floor/walls.

Need water resistance: in water, velocity damping and maybe fragment slows. Draw.

Need crack drag:
Mouse events: if !crack.triggered and pointer inside tank.right +/- 25 and y between crackY ± crack.length maybe or within tank; set dragging. Update crack.y. Pre-burst crack length maybe 14 px; show dashed. If lower crack, label "stronger pressure". If high, "weak/above waterline". Trigger button.

Need UI:
- Canvas full.
- Overlay panel top left with title, instructions, crack level, flow rate, waterline height. Buttons: Trigger burst, Reset. Maybe slider? User can drag crack; button.
- Need no external resources.
- Maybe "Drag crack vertically" prompt.

Need implement all in one file. Need likely large code. Need ensure no syntax errors. Need test with node? We can run maybe no browser. But can inspect.

Need perhaps include "large glass aquarium whose side panel develops a visible crack and then bursts" visually. Need draw aquarium glass: transparent panels, reflections, highlights, frame. Before burst: crack on right side. After burst: hole with jagged edges, fragments.

Need code.

Let's start writing index.html. But first maybe ls?
bash {"command":"ls -la /tmp/ws","timeout":10}
toolResult

total 0 drwxr-sr-x. 2 node node 6 Aug 2 15:58 . drwxrwsrwx. 3 root node 16 Aug 2 15:58 ..

assistant
thinking
Need write file. Need create robust code. Let's craft carefully.

Need maybe use canvas with devicePixelRatio scaling. Use CSS. Need code.

Let's plan code structure:

HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Cracking Aquarium Burst Simulation</title>
<style>
html,body { margin:0; height:100%; overflow:hidden; background:#111827; font-family: ...}
#wrap { position:relative; width:100vw; height:100vh; }
canvas { display:block; width:100%; height:100%; background: radial-gradient(...); cursor: crosshair; }
.panel { position:absolute; top:16px; left:16px; ... backdrop-filter? no external. }
button...
.badge...
</style>
</head>
<body>
<div id=wrap>
<canvas id=scene></canvas>
<div class=panel>...</div>
</div>
<script>
(() => {
'use strict';
const canvas = document.getElementById('scene');
const ctx = canvas.getContext('2d');
const DPR = Math.min(window.devicePixelRatio || 1, 2);
const VW=1000,VH=680;
function resize() { canvas.width=VW*DPR; canvas.height=VH*DPR; canvas.style.width='100vw'; canvas.style.height='100vh'; ctx.setTransform(DPR,0,0,DPR,0,0); }
window.addEventListener('resize', resize); resize();

const cfg = {roomLeft:35, roomRight:965, floorY:620, leftWallX:25, waterDensity:1};
const tank = {x:60,y:72,w:620,h:503,bottom:575,right:680,top:72};
let waterlineY = tank.y + 82; // initial
const gravity = 980;
let waterDepth = tank.bottom - waterlineY;
let waterVolume = waterDepth * tank.w;
let crack = {x:tank.right+3,y:tank.y+292,length:0,maxLength:105,triggered:false,dragging:false,age:0,seed:...};
let time=0,last=performance.now();
let flowRate=0;
let particles=[], puddles=[], fragments=[], objects=[], fishMessages=[] maybe.

function reset() { ... }
Need initialize.

Need random deterministic maybe Math.random. Reset.

Objects:
const objects = [
{type:'fish', name:'fish', x:180,y:290,vx:-45,vy:10,r:18,density:0.98, state:'underwater', swimPhase:0, swept:false, alive:true, mass:1},
{type:'rock', x:310,y:520,vx:0,vy:0,r:25,density:2.7,state:'underwater'},
{type:'rock2', ...},
{type:'toy', x:430,y:140,vx:15,vy:0,r:22,density:0.35,state:'underwater'},
{type:'plant', anchorX:130, anchorY:tank.bottom-15, height:80, phase:0, sway:0, density:0.8}
]
Plants anchored maybe array of plant.

Need draw plants.

Current:
function currentAt(x,y) {
const depth = Math.max(0, waterlineY - crack.y);
if (depth <= 0 || !crack.triggered) return {vx:0,vy:0,strength:0};
if (y < waterlineY) return {vx:0,vy:0,strength:0};
const pressure = clamp(depth/(tank.bottom-waterlineY),0,1);
const dx = tank.right - x;
if (dx < -10) return {0};
const rangeX = 280;
const rangeY = 180;
const influence = pressure * Math.exp(-dx*dx/(2*rangeX*rangeX)) * Math.exp(-((y-crack.y)*(y-crack.y))/(2*rangeY*rangeY));
return {vx: influence*(320+420*Math.sqrt(depth/360)), vy: -influence*35, strength:influence};
}
Maybe for toy/fish.

Water flow:
function updateWater(dt) {
if (!crack.triggered) { flowRate=0; return; }
const depth = Math.max(0, waterlineY - crack.y);
const openingHeight = crack.length;
const openingWidth = 8 + crack.length*0.35;
const area = openingHeight * openingWidth;
const velocity = Math.sqrt(2*gravity*depth);
flowRate = 0.38 * area * velocity; // px^2/s
waterDepth = Math.max(0, waterDepth - (flowRate*dt)/tank.w);
waterlineY = tank.bottom - waterDepth;
if (waterlineY < crack.y) { // no more flow
// but if waterline below crack, flow 0
}
}
Need if waterlineY can go below bottom? waterlineY = bottom - waterDepth, clamp.

Need spawn jet based on flowRate. Use accumulator. But flowRate changes. spawnAccumulator += flowRate*dt / 12; while spawnAccumulator>1 spawn. Need not too many. Jet particles have radius maybe random 1-3.5, color.

function spawnJetParticles(dt) {
if (!crack.triggered) return;
const depth = waterlineY - crack.y; if depth<=0 return;
const openingHeight = crack.length;
let count = flowRate*dt/18; spawnAccumulator += count; while spawnAccumulator>1 { spawn; spawnAccumulator--; }
}

Particle update:
p.age += dt; p.x += p.vx*dt; p.y += p.vy*dt; p.vy += gravity*dt; p.vx *= Math.pow(0.995, dt*60);
if p.y>floorY-p.r: p.y=floorY-p.r; p.vy=-Math.abs(p.vy)*0.22; p.vx*=0.62; p.r*=0.985; if p.y...
if p.x>roomRight-p.r: p.x=roomRight-p.r; p.vx=-Math.abs(p.vx)*0.25; p.vy*=0.7;
if p.x<roomLeft+p.r: p.x=...
if p.age>4 remove.
Add puddle particle if hits floor:
puddles.push({x:p.x,y:floorY-r,vx:p.vx*0.45,vy:0,r:p.r*1.3,age:0,maxAge:8+...,spread:0});
Maybe spawn puddle directly from flow at breach? Need puddle spreading. Add new puddle particle each hit. If no jet hitting, puddle particles spread.

Puddle update:
for each p: p.age += dt; p.r += p.spread*dt; p.x += p.vx*dt; p.vx *= Math.pow(0.992,dt*60); p.vx += (Math.random? no) maybe radial spreading.
Collisions:
if p.x-p.r<roomLeft: p.x=roomLeft+p.r; p.vx=Math.abs(p.vx)*0.2;
if p.x+p.r>roomRight: p.x=roomRight-p.r; p.vx=-Math.abs(p.vx)*0.2;
if p.y+p.r>floorY: p.y=floorY-p.r; p.vy=0;
p.r = Math.min(p.r + p.spread*dt, p.maxR);
remove if age>maxAge.
Draw puddles as radial gradient or ellipse.

Glass fragments spawn:
function spawnFragments() {
const count=120;
for i:
const len = crack.maxLength;
const p = poly random;
fragments.push({x:tank.right+random(-8,8), y: crack.y + random(-len/2,len/2), vx: random(-120,280), vy: random(-240,80), r: random(4,12), mass: r*r*0.08, rot: random(0,2pi), vr: random(-8,8), omega:..., points: makeTriangle? , w: r*1.2,h:r*1.2, density:2.5, state:'airborne', water:0});
}
Need angular velocity.

Fragment update:
for f:
f.age += dt; f.rot += f.omega*dt;
const inWater = f.y > waterlineY && f.y < tank.bottom+50;
const drag = inWater ? 2.8 : 0.08;
f.vx *= Math.exp(-drag*dt); f.vy *= Math.exp(-drag*dt);
f.vy += gravity*dt;
if (inWater) { f.vy -= gravity*(waterDensity/f.density)*0.15*dt; } // slight buoyancy, but glass sinks.
// collisions with room boundaries and tank boundaries. Need choose boundary based on x:
if f.x - f.r < roomLeft || f.x+f.r>roomRight || f.y+f.r>floorY:
if f.x-r<roomLeft: f.x=roomLeft+r; f.vx=Math.abs(f.vx)*0.35; f.omega*=0.8
if f.x+r>roomRight: f.x=roomRight-r; f.vx=-Math.abs(f.vx)*0.35
if f.y+r>floorY: f.y=floorY-r; f.vy=-Math.abs(f.vy)*0.25; f.vx*=0.75; f.omega*=0.8
if f.y-r<70 maybe ceiling? no.
if f.y < tank.y-40 maybe f.vy += gravity? bounce? maybe no.
// tank internal collisions for fragments inside tank
if f.x-f.r < tank.x+8 and f.x < tank.right+20: f.x=tank.x+8+r; f.vx=Math.abs(f.vx)*0.4;
if f.x+f.r > tank.right-8 and f.x < tank.right+40: f.x=tank.right-8-r; f.vx=-Math.abs(f.vx)*0.4;
if f.y+f.r > tank.bottom+10 and f.x<tank.right+40: f.y=tank.bottom+10-r; f.vy=-abs(f.vy)*0.25;
// fragment-fragment collisions:
for j>i: dx,dy,dist; if dist<rsum:
nx,ny; penetration; separate; relv = (vj-vi) dot n; if relv<0: impulse = -(1+0.25)*relv/(invMass sum); apply.
omega damping.
Remove if age>20 or offscreen.
}
Need maybe if fragments in water resistance.

Object update:
function updateObjects(dt) {
for obj of objects:
if obj.type==='plant' continue;
const prevState=obj.state;
const inWater = obj.y > waterlineY && obj.y < tank.bottom + obj.r; // center below surface
if (inWater) obj.state='underwater'; else obj.state='airborne';
// if object is in leak breach? maybe if obj.type fish/toy and obj.x > tank.right-12 and current strength > threshold
const cur = currentAt(obj.x,obj.y);
const nearLeak = Math.abs(obj.y-crack.y)<120 && obj.x>400;
if (obj.type==='fish' && obj.state==='underwater' && nearLeak && cur.strength>0.08) {
obj.swimPower = 110 + 100*cur.strength; // swim left
// if current strong enough > maybe, net right
} else if (obj.type==='fish' && obj.state==='underwater') {
obj.swimPower=45;
}
if (obj.type==='toy' && obj.state==='underwater') {
// buoyancy keeps near surface; it drifts with current more
}
// Forces
obj.vx += cur.vx * obj.dragWater * dt; obj.vy += cur.vy*obj.dragWater*dt;
if (obj.state==='underwater') {
// buoyancy
const b = (1 - obj.density) * gravity * 0.02? Need scale.
obj.vy -= (1 - obj.density) * gravity * 0.35 * dt; // if density<1, upward; density>1 downward
// drag to current
const wx=cur.vx - obj.vx; obj.vx += wx * obj.waterDrag * dt;
const wy=cur.vy - obj.vy; obj.vy += wy * obj.waterDrag * dt;
} else {
// low drag
obj.vx *= Math.pow(0.995, dt*60); obj.vy *= Math.pow(0.995, dt*60);
}
// fish swim acceleration
if (obj.type==='fish' && obj.state==='underwater') {
const cur = currentAt(obj.x,obj.y);
if (nearLeak && cur.strength>0.08) {
obj.vx += -obj.swimPower*dt; // left
// if near breach, fish tries upward to avoid? maybe obj.vy -= 20*dt
} else {
obj.vx += Math.sin(time*2+obj.phase)*20*dt;
obj.vy += Math.cos(time*1.5+obj.phase)*10*dt;
}
}
// toy: surface follow
if (obj.type==='toy' && obj.state==='underwater') {
const targetY = waterlineY + obj.r*0.15;
const surfaceError = targetY - obj.y;
obj.vy += surfaceError * 0.8 * dt; // positive if below target -> down? Wait y positive down, if obj.y > targetY, need upward negative. So vy += -surfaceError*0.8*dt.
obj.vy += Math.sin(time*3)*8*dt;
}
// rocks: sink
if (obj.type==='rock') { obj.vy += 18*dt; }
// update
obj.x += obj.vx*dt; obj.y += obj.vy*dt;
// breach condition
if ((obj.type==='fish' || obj.type==='toy') && obj.state==='underwater' && obj.x > tank.right - 12 && cur.strength > (obj.type==='fish'?0.18:0.30)) {
obj.state='airborne'; obj.x=tank.right+obj.r*0.5; obj.vx += cur.vx*1.6; obj.vy += cur.vy*1.6; obj.inAirTime=0;
}
// collisions
if (obj.state==='underwater') {
if (obj.x - obj.r < tank.x + 10) { obj.x=tank.x+10+obj.r; obj.vx=Math.abs(obj.vx)*0.45; }
if (obj.x + obj.r > tank.right - 10) { obj.x=tank.right-10-obj.r; obj.vx=-Math.abs(obj.vx)*0.35; }
if (obj.y + obj.r > tank.bottom - 4) { obj.y=tank.bottom-4-obj.r; obj.vy=-Math.abs(obj.vy)*0.25; if (obj.type==='rock') obj.vy=0; }
if (obj.y - obj.r < waterlineY) { // surface crossing? no collision; if toy surface follow.
}
} else {
obj.inAirTime += dt;
if (obj.x - obj.r < roomLeft) { obj.x=roomLeft+obj.r; obj.vx=Math.abs(obj.vx)*0.35; }
if (obj.x + obj.r > roomRight) { obj.x=roomRight-obj.r; obj.vx=-Math.abs(obj.vx)*0.35; }
if (obj.y + obj.r > roomFloorY) { obj.y=roomFloorY-obj.r; obj.vy=-Math.abs(obj.vy)*0.25; obj.vx*=0.75; }
if (obj.y - obj.r < 0) { obj.y=obj.r; obj.vy=Math.abs(obj.vy)*0.2; }
}
// if tank drains and waterline below object? state airborne
if (waterlineY > obj.y && obj.state==='underwater') { obj.state='airborne'; }
}
}
Need if objects state underwater but waterline lowers below y? State airborne. For toy floats, if waterline below? It might be above waterline and in air, then falls.

Need currentAt uses waterline and crack; if waterline below crack, zero.

Need fish "attempting to swim against current before being swept through breach" maybe draw fish tail and maybe label "fish fighting current" when near leak. We can include fishMessage text.

Plants:
function updatePlants(dt) { for plant: const cur=currentAt(plant.x, plant.y); plant.sway = Math.sin(time*2+plant.phase)*0.2 + cur.vx*0.002; draw as stems from anchor with segments, bend. }
Need draw.

Draw:
function draw() {
ctx.clearRect...
drawRoom()
drawTankBack maybe glass panels.
drawWater()
drawObjects()
drawCrackOpening()
drawJet()
drawPuddle()
drawFragments()
drawHUD overlays? maybe UI in DOM.
}

Room:
- background with subtle floor/walls. Draw left/right walls as vertical lines, floor rectangle. Puddle on floor.
- Aquarium: draw outer frame with rounded rect. Glass panels: left panel, back panel, bottom, top, right panel. Use translucent fill. Draw water inside with clipping to tank interior.
Need draw water after tank back before glass? In side view:
1. room
2. tank glass exterior/back
3. water region clipped to tank interior: blue gradient from waterlineY to bottom.
4. underwater objects/plants.
5. water surface line.
6. glass panels/reflections/frame.
7. crack and opening on right panel.
8. jet particles and fragments outside.
Maybe draw jet after opening and before fragments? Water jet should be visible in front. Draw puddle after floor.

drawTank:
ctx.save(); roundRect(tank.x,tank.y,tank.w,tank.h,16); fill rgba glass; stroke.
Draw water clip: ctx.rect(tank.x+8, waterlineY, tank.w-16, tank.bottom-waterlineY); fill gradient. Need waterline line.
Need bottom.
Draw water surface: line from tank.x+8 to tank.right-8 at waterlineY; gradient highlight.
Maybe draw water bubbles.

drawCrack:
if !triggered: draw dashed crack line at crack.x (right panel) y length 18; draw small glass crack branches; show draggable handle.
if triggered: draw opening/hole:
const len=crack.length; x=tank.right+2; top=crack.y-len/2, bottom=...
Draw jagged hole on right panel: black/blue dark polygon with points around opening.
Draw side panel fracture lines from opening. Draw fragments maybe.
Need "visible crack develops and then bursts": after trigger, crack length grows. Use crack.length = Math.min(max, crack.length + dt*110) maybe. Pre length maybe 18.

Need water jet spawn from opening. Draw particles:
for p of particles: radial gradient with alpha.

Need puddle: draw wet floor ellipse maybe. For each puddle, radial gradient.

Need draw objects:
- fish: if state underwater, draw orange fish at x,y with tail oscillation. If airborne, maybe flip.
- rocks: irregular polygon with shading.
- toy: floating toy (rubber duck) maybe draw yellow duck. It reacts buoyancy; if underwater, partially submerged? draw bob.
- plants: draw green stems/leaves sway.

Need draw HUD:
DOM update every frame:
document.getElementById('status').innerHTML = ...
Need maybe update crackStrength label:
const depth = waterlineY-crack.y; const strength = flowRate; if !triggered "draggable crack: lower = stronger"; if triggered "jet velocity..."
Need maybe lower crack initial stronger. Include "Drag crack vertically; lower crack = higher pressure."

Need reset.

Need pointer events:
canvas.addEventListener('pointerdown', e => { const p=getCanvasPoint(e); if (!crack.triggered && Math.abs(p.x-crack.x)<35 && p.y>crack.y-crack.length/2-20 && p.y<crack.y+crack.length/2+20) { crack.dragging=true; setCrackY(p.y); canvas.setPointerCapture(e.pointerId);}
});
pointermove: if dragging update.
pointerup: dragging=false.
Need if trigger button pointer? Canvas no.

Button handlers.

Potential issue: CSS backdrop-filter maybe not necessary.

Need maybe use no external fonts.

Need final answer just mention file. But user asked "Give me 1 html file". We need output code? Since writing file in /tmp/ws. Final can say created /tmp/ws/index.html and maybe include not code? Usually we should provide file path. But maybe include concise. Need ensure index.html present. We can final say "Created /tmp/ws/index.html". But maybe user expects code? In coding tasks, final should be brief with file path. Could include not full code to avoid too long. But maybe desired overwordiness medium. We can mention features. Need no extra.

Need write file. Let's craft code. Need be careful with syntax.

Let's compose index.html.

Potential use ctx.roundRect; widely supported but maybe okay. Could implement helper roundedRect for compatibility. We'll define function roundRect(ctx,x,y,w,h,r) using path.

Need helper clamp, lerp, rand, polar.

Detailed code:

```
(function(){
const canvas=...
const ctx=...
const W=1000,H=680;
const DPR=...
function resize() { canvas.width = Math.round(W*DPR); canvas.height=...; ctx.setTransform(DPR,0,0,DPR,0,0); }
...
const room = {left:35,right:965,floorY:620};
const tank = {x:60,y:70,w:620,h:505,bottom:575,right:680,top:70,wall:14};
let waterlineY = tank.y+82;
let waterDepth = tank.bottom - waterlineY;
let flowRate=0;
let crack = {x:tank.right+4, y:tank.y+300, length:18, maxLength:112, triggered:false, dragging:false, age:0, maxDepth: tank.bottom-waterlineY};
let spawnAcc=0;
let particles=[], puddles=[], fragments=[], objects=[], plants=[];
...
function reset(){
 waterlineY=tank.y+82; waterDepth=...
 crack={... length:18,...}
 particles=[]; puddles=[]; fragments=[];
 objects = [
  {type:'fish', name:'fish', x:170,y:300,... state:'underwater', swimPower:55, phase:0, inAirTime:0},
  {type:'rock',...},
  {type:'rock',...},
  {type:'toy',...},
  {type:'fish', maybe second?}
 ];
 plants = [
  {type:'plant', anchorX:120, anchorY:tank.bottom-18, height:90, width:20, phase:0, segments:7, sway:0, density:0.75},
...
 ];
}
```
Need maybe multiple fish. The requirement fish plural? "Fish, rocks, plants, and a floating toy" could be one fish but plural maybe okay. Add 3 fish maybe.

Need object densities:
fish density 0.98, mass 1, waterDrag 1.8, airDrag 0.02? Actually for object update.

```
function objectDefaults(type) {
 if fish: {r:17,density:0.98, waterDrag:1.1, state:'underwater'}
 rock: {r:22,density:2.4, waterDrag:0.6}
 toy: {r:21,density:0.35, waterDrag:0.9}
}
```

Need updateObjects:
Let's refine.

```
function updateObjects(dt){
 for (const o of objects) {
   if (o.type==='plant') continue;
   const cur = currentAt(o.x,o.y);
   const wasUnderwater = o.state === 'underwater';
   const inWater = (o.y > waterlineY && o.y < tank.bottom + o.r) || (o.type==='toy' && o.y > waterlineY - o.r);
   if (inWater) o.state='underwater'; else o.state='airborne';

   if (o.state==='underwater') {
      const b = (1 - o.density) * gravity * 0.35; // upward if less dense
      o.vy += b * dt;
      const wx = cur.vx - o.vx;
      const wy = cur.vy - o.vy;
      o.vx += wx * o.waterDrag * dt;
      o.vy += wy * o.waterDrag * dt;
      if (o.type==='fish') { ... }
      if (o.type==='toy') { const target=waterlineY+o.r*0.2; o.vy += -(o.y-target)*1.1*dt + Math.sin(time*3)*6*dt; }
   } else {
      o.vx *= Math.pow(0.995, dt*60);
      o.vy *= Math.pow(0.995, dt*60);
      o.vy += gravity * 0.7 * dt;
   }
   if (o.type==='rock' && o.state==='underwater') { o.vy += 12*dt; }
   if (o.type==='fish' && o.state==='underwater') {
      const near = Math.abs(o.y-crack.y)<130 && o.x>360 && cur.strength>0.05;
      if (near) {
        o.swimPower = 115 + 120*cur.strength;
        o.vx += -o.swimPower*dt;
        o.vy += -15*dt;
      } else {
        o.vx += Math.sin(time*2.4+o.phase)*22*dt;
        o.vy += Math.cos(time*1.8+o.phase)*14*dt;
      }
   }
   // breach condition:
   if ((o.type==='fish'||o.type==='toy') && o.state==='underwater' && o.x>tank.right-18 && cur.strength > (o.type==='fish'?0.15:0.25)) {
       o.state='airborne'; o.x=tank.right+o.r*0.7; o.vx += cur.vx*1.8; o.vy += cur.vy*1.8; o.inAirTime=0;
   }
   o.x += o.vx*dt; o.y += o.vy*dt;
   // collisions
   if (o.state==='underwater') { ... } else { ... }
   if (waterlineY > o.y && o.state==='underwater') { o.state='airborne'; } // after move?
 }
}
```
Need if toy density low and waterline lowers below it, it becomes airborne. But toy surface target maybe.

Need fish "attempting to swim against current before being swept through breach" maybe in draw: if near leak, draw small arrows against current? Could draw fish tail stronger. Maybe not needed. But maybe add current arrows in water.

Need currentAt:
```
function currentAt(x,y) {
 let depth = Math.max(0, waterlineY - crack.y);
 if (depth<=0 || !crack.triggered || y < waterlineY) return {vx:0,vy:0,strength:0};
 const pressure = clamp(depth / Math.max(1, tank.bottom-waterlineY),0,1);
 const dx = tank.right - x;
 if (dx < -20) return ...
 const rangeX = 320;
 const rangeY = 220;
 const distX = dx*dx/(2*rangeX*rangeX);
 const distY = (y-crack.y)*(y-crack.y)/(2*rangeY*rangeY);
 const influence = pressure * Math.exp(-distX - distY);
 return {vx: influence*(300+520*Math.sqrt(depth/420)), vy: influence*(-30+10*Math.sin(time*4)), strength:influence};
}
```
If dx negative far left, influence tiny. Good.

Need updateWater:
```
function updateWater(dt) {
 if (!crack.triggered) { flowRate=0; return; }
 crack.length = Math.min(crack.maxLength, crack.length + dt*115);
 const depth = Math.max(0, waterlineY - crack.y);
 if (depth>0) {
   const openingHeight = crack.length;
   const openingWidth = 8 + crack.length*0.38;
   const area = openingHeight*openingWidth;
   const v = Math.sqrt(2*gravity*depth);
   flowRate = 0.34*area*v;
   waterDepth = Math.max(0, waterDepth - (flowRate*dt)/tank.w);
   waterlineY = tank.bottom - waterDepth;
 } else { flowRate=0; }
 spawnAccumulator += flowRate*dt/14;
 while (spawnAccumulator>=1) { spawnJetParticle(); spawnAccumulator--; }
}
```
Need if waterline below crack, no flow. But waterline might still be above crack until tank drains below crack. Good.

SpawnJetParticle:
```
const depth = Math.max(0,waterlineY-crack.y);
const len=crack.length;
const y = crack.y + rand(-len*0.45,len*0.45);
const spread = len*0.18;
const strength = Math.sqrt(clamp(depth/420,0,1));
particles.push({x:tank.right+8+rand(-3,3), y, vx:(360+560*strength)+rand(-90,80), vy:rand(-45,45), r:rand(1.2,3.4), age:0, life:rand(2.5,4.5), spin:rand(-5,5)});
```
Need if depth low, weak: vx maybe 160+260*strength. Lower crack stronger.

Draw water jet maybe with gradient.

Need updatePuddle:
```
function updatePuddle(dt) {
 for p of puddles:
  p.age += dt; p.r += p.spread*dt; p.x += p.vx*dt; p.vx *= Math.pow(0.985,dt*60); p.vy += 0?;
  if p.x-p.r<room.left: ...
  if p.x+p.r>room.right: ...
  if p.y+p.r>room.floorY: p.y=room.floorY-p.r; p.vy=0;
  if (p.age>p.maxAge) remove.
}
```
Need "spreading puddle collides with room boundaries": show p.r expanding and bouncing at walls. Good.

Need drawPuddle:
```
ctx.save(); for p: const g=ctx.createRadialGradient(p.x,p.y,p.r*0.1,p.x,p.y,p.r); g.addColorStop...; ctx.globalAlpha = 0.22*(1-p.age/p.maxAge); ctx.beginPath(); ctx.ellipse(p.x,p.y,p.r,p.r*0.42,0,0,2pi); fill; ctx.restore();
```
Maybe draw floor wet streak.

Need draw room:
```
const bg=ctx.createLinearGradient(0,0,0,H); ... fillRect
draw wall lines
floor rectangle with gradient
draw floor grid
```

Need drawTank:
```
ctx.save();
ctx.beginPath(); roundedRect(tank.x,tank.y,tank.w,tank.h,18); ctx.clip();
ctx.fillStyle = rgba glass; fillRect
// back inside
ctx.fillStyle = '#0e355a'; fillRect(tank.x+12,tank.y+12,tank.w-24,tank.h-24)
// water
const waterGrad = ctx.createLinearGradient(0,waterlineY,0,tank.bottom);
waterGrad.addColorStop(0, 'rgba(60,190,255,0.55)');
waterGrad.addColorStop(1, 'rgba(0,80,170,0.72)');
ctx.fillStyle=waterGrad; ctx.fillRect(tank.x+10, waterlineY, tank.w-20, tank.bottom-waterlineY+10);
// bubbles
for i? Could generate static bubbles based on sin? Need deterministic. Maybe array bubbles reset. Create bubbles in reset with x,y,r,phase.
```
Bubbles:
`bubbles = Array.from({length:50},...)`
Draw inside water:
```
for b: let y=b.y + Math.sin(time*0.8+b.phase)*5; let x=b.x+Math.sin(time*1.2+b.phase)*3; stroke alpha.
```
Need if waterline lower, bubbles below waterline only.
Draw waterline after clip:
```
ctx.restore();
ctx.strokeStyle...
ctx.beginPath(); moveTo(tank.x+10,waterlineY); lineTo(tank.right-10,waterlineY); stroke;
draw surface highlights.
```
Need maybe draw waterline continuous.

Draw objects after water? If water transparent, objects visible. If inside water, draw with slight offset? Good.

Draw glass panels:
- Outer frame: stroke with rgba.
- Right panel: before burst transparent; after burst maybe dark hole.
- Reflections: slanted lines.

Need draw crack/hole:
```
function drawCrackAndBurst(){
 ctx.save();
 if (!crack.triggered) {
   drawCrackBranch(crack.x, crack.y, crack.length, alpha)
   draw draggable handle
 } else {
   const len=crack.length;
   const x=tank.right+6, top=crack.y-len/2, bottom=crack.y+len/2;
   ctx.fillStyle='rgba(0,0,0,0.35)';
   // jagged hole polygon
   const pts = makeBurstHole(x,top,bottom,len)
   fill
   // draw blue inside water at opening
   ctx.fillStyle='rgba(12,105,180,0.75)';
   fill path
   // jagged glass edges
   ctx.strokeStyle='rgba(220,250,255,0.8)';
   ...
   draw fracture lines from opening to top/bottom
 }
 ctx.restore();
}
```
Need makeBurstHole deterministic based on crack? Could use sin.

Maybe use `makeHolePoints(x,top,bottom,len)` with 8 points:
```
for i 0..7: a=i/8*2pi; rx=5+sin(i*12.3)*4; ry=len*0.5+sin(i*4.7)*5; return {x:x+rx*cos(a), y:top+ry*sin(a)}
```
Need ensure x coordinate right panel. Good.

Draw crack branches:
function drawCrackBranch(cx,cy,angle,len,depth) {
 for branch i:
  const branches = [...]
  ctx.beginPath(); moveTo; lineTo...
}
Before trigger length=18. After trigger branches longer.

Need draw current arrows maybe:
```
function drawCurrentArrows(){
 if !triggered return;
 for y from waterlineY+40 to bottom step 55:
   const strength = currentAt(tank.x+350,y).strength;
   if strength>0.15 draw arrow from x to x+strength*20
```
This shows fish swimming against current.

Need draw glass fragments after crack and jet? Maybe fragments in front.

Need draw HUD DOM:
```
const status = document.getElementById('status');
function updateHUD(){
 const depth = Math.max(0,waterlineY-crack.y);
 const pressure = Math.sqrt(depth);
 const pct = clamp(waterDepth/(tank.bottom-tank.y),0,1);
 status.innerHTML = `...`;
}
```
Need maybe if lower crack label:
```
const pressureText = depth>0 ? `${Math.round(Math.sqrt(depth))} px√g` : 'no head pressure';
```
Flow rate in "px²/s" maybe.

Need ensure no external network. No images.

Potential issue: canvas transform after resize; if using DPR and setTransform, but after resize, canvas style width 100vw. We set canvas.width=VW*DPR and height=VH*DPR. CSS width=100%, height=100%. In JS drawing uses virtual coordinates, transform DPR. Good.

Need pointer coordinates with virtual coordinates:
```
function pointerPos(e) {
 const rect=canvas.getBoundingClientRect();
 return {x:(e.clientX-rect.left)*VW/rect.width, y:(e.clientY-rect.top)*VH/rect.height};
}
```
Need set pointer capture.

Need maybe if pointerdown on UI button not canvas? okay.

Need buttons:
```
document.getElementById('trigger').addEventListener('click', () => { crack.triggered=true; crack.age=0; spawnFragments(); });
document.getElementById('reset').addEventListener('click', reset);
```
Need if reset while pointer? okay.

Need update `crack.y` clamp:
```
function setCrackY(y){ crack.y=clamp(y, tank.y+45, tank.bottom-70); }
```
Need crack.x = tank.right+4.

Need `crack.length` pre = 18. During trigger grows.

Need "side panel develops visible crack and then bursts." Maybe before trigger crack visible. On trigger, burst. Good.

Need maybe "lower crack should initially produce stronger jet" - if user moves crack lower, initial depth increases. But if crack lower than bottom? clamp. Good.

Need maybe "flow strength based on water depth and decreasing as tank drains" - code.

Need "curved water jet affected by gravity" - particle gravity.

Need "spreading puddle collides with room boundaries" - puddle particles.

Need "Fish, rocks, plants, and a floating toy reacting differently according to density, buoyancy, drag, and current" - code.

Need "Objects transitioning correctly from underwater motion to airborne motion and then to floor collisions" - code.

Need "Fish attempting to swim against current before being swept through breach" - code.

Need "Glass fragments with angular velocity, collisions, and water resistance" - code.

Need "visible waterline lowers continuously rather than disappearing all at once" - code.

Potential issue: waterline is y coordinate; lower means increasing. Good.

Need maybe "room boundaries" include floor and side walls; puddle collides.

Need maybe "floating toy" drawn as duck. Good.

Need maybe "plants" anchored; not density? Could mention in code comments.

Need maybe "large aquarium" visual.

Let's write.
write {}
assistant
thinking
Need create file. We'll write. Need careful code. Let's design in detail.

HTML skeleton:
<!doctype html><html lang="en"><head><meta charset...><title>...</title><style> html,body {margin:0; height:100%; overflow:hidden; background:#...; font-family:...;} canvas {display:block; width:100vw; height:100vh; cursor:crosshair;} .hud {position:fixed; top:16px; left:16px; ...} button etc. .legend maybe.
</style></head><body>
<canvas id="scene" width="1000" height="680"></canvas>
<div class="hud">...</div>
<script>
(() => {
'use strict';
const canvas = document.getElementById('scene');
const ctx = canvas.getContext('2d');
const W=canvas.width,H=canvas.height;
const DPR maybe for high res? We can set internal size fixed and CSS scaling. Could handle devicePixelRatio by setting canvas.width=W*dpr etc and ctx.scale? But easier fixed. Need crisp. Could implement resize? Not necessary. But for actual display, CSS scales. Could use dpr? More complex but okay. Since W/H fixed, canvas width/height attributes 1000/680. CSS 100vw/vh.

const room = {left:35,right:965,floor:620,back:25};
const tank = {x:60,y:70,w:620,h:505,bottom:575,right:680};
const gravity=980;
let waterline = tank.y+78; // surface
let waterDepth = tank.bottom - waterline;
let crack = {x:tank.right,y:360,length:16,maxLength:95,triggered:false,dragging:false,age:0,active:false};
let flowRate=0;
let waterVolume = waterDepth*tank.w; // cross-section px^2
...
Need maybe tank bottom at y=575, room floor at 620. Tank bottom maybe transparent, water can spill to floor? Opening on side at x=tank.right, water exits horizontally to right, hits floor at y=620.

Reset:
function reset() { waterline=150; waterDepth=425; waterVolume=425*tank.w; waterlineLabel; crack y=355? length=18 triggered=false; particles=[]; puddle=[]; fragments=[]; objects reset. }
Need objects reset:
fish = {type:'fish', x:210, y:320, vx:-60, vy:10, r:18, density:0.98, state:'underwater', targetPhase, alive:true};
rock1 = {type:'rock', x:155, y:500, vx:0,vy:0,r:26,density:2.6,state:'underwater'};
rock2 = ...
plant = {type:'plant', anchorX:285, anchorY:tank.bottom-12, len:105, width:12, sway:0, phase:1};
toy = {type:'toy', x:430,y:190,vx:20,vy:0,r:22,density:0.35,state:'underwater'};
Maybe plants anchored but current bends.

Need object array maybe fish, rock1, rock2, toy. Plants separate.

Simulation step dt capped 0.033.
function update(dt):
  updateCrack(dt)
  computeFlow(dt)
  updateWaterline
  spawn jet
  update jet particles
  update puddle
  update objects
  update plants
  update fragments
  update labels

Flow:
function computeFlow(dt) {
  if (!crack.triggered) {flowRate=0; return;}
  const depth = Math.max(0, waterline - crack.y);
  const maxDepth = tank.bottom - waterline; // maybe
  const leakFraction = easeOut? crack.length / crack.maxLength; // current opening
  const orificeWidth = 8 + crack.length*0.22; // px
  const area = orificeWidth * crack.length; // 2D cross-section
  const v = Math.sqrt(2*gravity*Math.max(0, depth));
  const c = 0.62;
  const newFlow = area * v * c;
  flowRate = newFlow;
  const drain = newFlow * dt / tank.w;
  waterDepth = Math.max(0, waterDepth - drain);
  waterline = tank.bottom - waterDepth;
  if (waterline < tank.y+8) waterline=tank.y+8;
  if (waterline <= crack.y) flowRate=0; // no pressure. But if waterline equal, no.
}
Potential issue waterline lower as water drains; if waterline crosses crack? Opening below waterline initially. If waterline goes below crack, no leak. Good.
But if waterline drops below crack, crack may be above waterline? Actually lower crack near bottom, tank drains a lot before waterline below crack. Good.

Jet spawn:
const maxDepth = tank.bottom - waterline; const normalized = clamp(depth / maxDepth,0,1); speed=180+420*sqrt(normalized); if depth > 1:
spawnCount = flowRate*dt/18; // maybe number.
for i<spawnCount create p.
But flowRate can be high: area 95*28=2660, v sqrt(2*980*200)=626, area*c=1034 px^2/s, spawnCount 0.6. Fine. If more particles okay.
p.r=random 2-5.
p.x=tank.right+8+random(-crack.length*0.15, crack.length*0.15)
p.y=crack.y + random(-crack.length*0.25, crack.length*0.25)
p.vx=speed + random(-40,40)
p.vy=random(-45,25) - 20*depth/maxDepth? Maybe upward slight.
p.age=0; p.life=2.5; p.puddle=false.
Update:
p.vy += gravity*dt; p.vx *= Math.pow(0.992, dt*60)? p.x += p.vx*dt; p.y += p.vy*dt.
If p.y >= room.floor - p.r: p.y=...; p.vy=-p.vy*0.25; p.vx*=0.72; p.age++; create puddle droplet if p.speed>20; if p.vx<0 maybe; if p.vx>0.
If p.x > room.right - p.r: p.x=...; p.vx=-p.vx*0.35; p.vy += gravity*dt; if p.y>floor-5 p.y=floor-p.r; 
If p.y > H+100 or age>life remove.
Maybe water jet should curve. Gravity.

Puddle:
function addPuddle(x,y,vx,vy,r) { puddle.push({x,y,r:random(2,8),vx:vx*0.45,vy:vy*0.1,age:0,life:18+random*12,spread:random(30,90)}); }
Update:
for p:
 p.age += dt; p.y=room.floor-p.r; p.x += p.vx*dt; p.vx *= Math.pow(0.92, dt*60); p.vx += p.spread*dt? Actually spread horizontal outward. Could have p.spreadVel. p.spreadVel += 8*dt; p.vx += p.spreadVel*dt; p.r += 0.2*dt maybe.
 Collide room left/right/floor. If p.age>life, p.alpha decays.
Draw puddle particles as ellipses.

Maybe also maintain "puddle" as water surface on floor: draw elliptical low alpha patches. Particles enough.

Current:
function currentAt(x,y) {
 let d = tank.right - x;
 let dy = y - crack.y;
 if (!crack.triggered) return {vx:0,vy:0,strength:0};
 let depth = Math.max(0, waterline - crack.y);
 if (depth <= 0) return ...
 let strength = clamp(depth/(tank.bottom-waterline),0,1);
 let dx = Math.exp(-Math.pow(d/230,2));
 let dyf = Math.exp(-Math.pow(dy/160,2));
 let falloff = Math.exp(-Math.pow((y-waterline)/180,1.5)); // stronger deeper? Actually current from crack.
 let speed = (180+420*Math.sqrt(strength)) * dx * dyf * strength;
 return {vx: speed, vy: -speed*0.08, strength:speed/(600)};
}
Maybe if x < tank.x+tank.w*0.15, current weak. If y above waterline no water. For fish, if y+? > waterline.
Current field in water only.

Objects:
function updateObjects(dt) {
  for each obj except plant:
    const inWater = obj.y > waterline + obj.r*0.2 && obj.y < tank.bottom - obj.r*0.5 && obj.x > tank.x+obj.r && obj.x < tank.right-obj.r;
    const c = inWater ? currentAt(obj.x,obj.y) : {0};
    const drag = inWater ? 1.4 : 0.08; // per second? Use damping = Math.exp(-drag*dt)
    if (obj.type==='fish') {
      if (inWater && !obj.swept) {
        const swim = currentAt(obj.x,obj.y).strength;
        obj.vy += (obj.type==='fish' ? -8 : 15)?? Actually fish swims upward? We'll implement swim acceleration:
        obj.vy += Math.sin(time*2+obj.phase)*10;
        obj.vx += -40*dt; // normal left swim
        if (obj.x > tank.x+tank.w*0.55 || Math.abs(obj.y-crack.y)<100) {
           obj.vx += (-180 - 120*swim)*dt; // swim against current
        }
        if (swim > 0.25 && obj.x > tank.right - 80) obj.swept=true;
      }
      // if swept and inWater, no swim, current pushes
    } else if (obj.type==='toy') {
      if (inWater) {
        obj.vy -= 35*dt; // buoyant upward
        obj.vx += Math.sin(time*1.5+obj.phase)*8*dt;
        if (c.strength > 0.15) obj.vx += c.vx*0.18*dt;
        if (obj.y < waterline + 20) obj.vy += 80*dt; // keep at surface
        if (c.strength > 0.35 && obj.x > tank.right - 120) obj.swept=true;
      }
    } else if (obj.type==='rock') {
      // sink
      if (inWater) obj.vy += 70*dt; // net downward
    }
    // buoyancy/density:
    if (inWater) {
      const eff = 1 - obj.density/1.0? Need waterDensity=1.0. For density<1, buoyancy upward acceleration = g*(1-density/waterDensity). For density>1, downward = g*(1 - waterDensity/density). We can compute:
      if (obj.density < 1) obj.vy -= gravity*(1 - obj.density)*0.9*dt;
      else obj.vy += gravity*(obj.density-1)*0.35*dt; // but rocks at bottom
      // drag toward current
      obj.vx += (c.vx - obj.vx) * (1 - Math.exp(-1.5*dt));
      obj.vy += (c.vy - obj.vy) * (1 - Math.exp(-1.2*dt));
    } else {
      obj.vx *= Math.exp(-0.15*dt);
    }
    // gravity always
    obj.vy += gravity*dt;
    // state transition
    if (inWater && obj.type==='fish' && !obj.swept && c.strength>0.25 && obj.x > tank.right-30) { obj.state='airborne'; obj.vx += c.vx*0.4; obj.vy += c.vy*0.4; }
    if (inWater && obj.type==='toy' && obj.swept && obj.x > tank.right-25) { state airborne; }
    if (!inWater && obj.state==='underwater') state='airborne'; // as waterline lowers
    // if state airborne and obj.x < tank.right maybe? Actually when x crosses tank.right. For toy maybe if x > tank.right - 10 and y > waterline? 
    // collisions
    if (obj.state==='underwater') collide with tank boundaries and bottom. if obj.x+r > tank.right-8 and obj.type not fish/toy? bounce. For fish if swept, allow exit. If toy swept, allow exit.
    if (obj.state==='airborne') collide room boundaries.
    obj.x += obj.vx*dt; obj.y += obj.vy*dt;
}
Need order collisions after movement. Need current before movement. For fish attempt swim. Need if fish exits breach:
- If obj.state==='underwater' && obj.x + obj.r > tank.right - 4 && obj.swept && obj.x < tank.right+50 then state='airborne'. But if not swept, tank wall collision.
- In collision step: if underwater and x+r > tank.right - 8 and obj.type==='fish' && obj.swept: state='airborne'; no bounce.
- if airborne and x > room boundaries.

Potential issue object inWater computed before movement, if x+r > tank.right, maybe not inWater. Need compute current before. We can use x within tank for current. If fish at breach, current high.

Underwater collision:
if obj.x - r < tank.x+8 { obj.x=tank.x+r+8; obj.vx=Math.abs(obj.vx)*0.55; }
if obj.y + r > tank.bottom-4 { obj.y=tank.bottom-r-4; if(obj.vy>0) obj.vy*=-0.25; obj.vx*=0.95; }
if obj.x + r > tank.right-8 { if (swept) state airborne; else { obj.x=tank.right-r-8; obj.vx=-Math.abs(obj.vx)*0.45; } }
if obj.y - r < waterline? water surface no collision.
Airborne:
if obj.x-r<room.left+r { ... } if obj.x+r>room.right-r { ... } if obj.y+r>room.floor { ... }
If object falls through crack outside tank and hits floor. Good.

Need fish drawn in different colors. Fish can be underwater or airborne. If airborne, maybe "airborne" label? Not necessary. Need maybe show trail.

Plants:
Plants anchored at bottom. Update sway = currentAt(anchorX,y).strength. Draw as stems from anchor to tip with quadratic curves offset by sway. Maybe multiple plants. Plants not move but react to current. Could include "plant" object in object list maybe anchored. Draw before objects? Plants behind objects. Use current.

Glass fragments:
function spawnFragments() {
 const count=90;
 const len=crack.maxLength;
 for i count:
  const y=crack.y + random(-len/2, len/2);
  const x=tank.right + random(-20, 10);
  const angle=random(0,2π);
  const size=random(4,14);
  const verts=[]; for j 0..3: r=size*random(0.6,1.2), a=angle+...; verts.push({x:Math.cos(a)*r,y:...});
  fragments.push({verts,x,y,vx:random(-120,240),vy:random(-220,60),rot:angle,omega:random(-8,8),size,mass:size*size,age:0,life:8+random*12,state:'airborne'});
}
Update:
for f:
 if f.age<0.05 maybe initial burst from crack; f.vx += random...
 const inWater = f.y > waterline + 5 && f.x < tank.right+20;
 f.vy += gravity*dt;
 if (inWater) {
   const damp=Math.exp(-2.2*dt); f.vx*=damp; f.vy*=damp; // water resistance
   f.vy -= gravity*(1 - 2.5)*0.25*dt; // glass density > water, net downward maybe
 } else { f.vx*=Math.exp(-0.05*dt); }
 f.x += f.vx*dt; f.y += f.vy*dt; f.rot += f.omega*dt; f.omega*=Math.exp(-0.4*dt);
 if (f.y > room.floor - f.size) { f.y=room.floor-f.size; f.vy*=-0.25; f.vx*=0.65; f.omega*=0.6; }
 if (f.x - f.size < room.left+f.size) { f.x=room.left+f.size; f.vx=Math.abs(f.vx)*0.35; f.omega*=-0.7; }
 if (f.x + f.size > room.right-f.size) { f.x=room.right-f.size; f.vx=-Math.abs(f.vx)*0.35; f.omega*=-0.7; }
 if (f.x < tank.x+5 && f.x>room.left) { ... } maybe tank left wall? But outside tank right no.
 // fragment-fragment collisions after loop maybe.
 if (f.y > H+100 || f.x<room.left-100 || f.x>room.right+100 || f.age>f.life) remove.
Need fragment-fragment collision:
for i<j:
 dx,dy,d; if d<minR:
  overlap=(minR-d)/2; nx=dx/(d||1); ny=...
  f1.x-=nx*overlap; f2.x+=nx*overlap...
  // impulse
  rvx=f2.vx-f1.vx; rvy=f2.vy-f1.vy; velAlong=nx*rvx+ny*rvy;
  if (velAlong<0) {
    impulse=-(1+0.2)*velAlong/(1/f1.mass+1/f2.mass)
    f1.vx -= impulse*nx/f1.mass; f2.vx += ...
    // friction angular
  }
Need maybe not too expensive 90 fragments -> 4005 pairs okay.

Draw:
function draw() {
 ctx.clearRect
 drawRoom()
 drawAquariumWater()
 drawPlants()
 drawObjects()
 drawCrackAndBurst()
 drawJet()
 drawPuddle()
 drawFragments()
 drawGlassPanels()
 drawHUD overlay maybe HTML.
}
Layering: Room background, floor/walls. Aquarium glass panel behind water? Need water inside transparent. Draw aquarium glass first? Sequence:
1. background wall.
2. floor/walls.
3. aquarium frame/back glass: draw tank glass translucent.
4. water inside: fill tank water region with blue gradient, clip to tank interior; draw bubbles, waterline.
5. plants (inside water), objects.
6. water surface line.
7. jet particles (outside).
8. puddle on floor (could be before/after objects? Puddle on floor after floor, before fragments maybe)
9. crack/burst opening and fragments.
10. glass highlights/frame.
Need puddle should be on floor outside tank; draw after floor and before fragments. Jet should appear in front. Glass fragments maybe in front.

Room:
draw background gradient. floor rectangle y=620. walls: left wall at x=35, right wall at x=965 maybe vertical lines. Draw floor tiles.

Aquarium:
- outer tank rectangle with rounded? Use fill transparent.
- glass panels: left, top, bottom, right. Right side panel with crack/hole.
- frame: stroke #9fb7c6, lineWidth 10.
- interior water: clip to tank rect, fill water from waterline to bottom. Use gradient. Draw wave line: sin.

Need waterline lower continuously: draw line at waterline with white-blue stroke, label maybe. Also draw "waterline" small tick.

Crack:
Before trigger:
draw side panel crack at crack.x, crack.y, length crack.length: jagged line, blue highlight, cursor.
After trigger:
draw burst hole: black/dark opening, jagged glass edges, fragments. Use crack.length grows with time. Draw a jagged vertical slit/hole at x=tank.right+4, y=crack.y. Opening height = crack.length. Draw edges as random-ish jagged polygon. Need deterministic? Use precomputed crackJags maybe. Could generate random each reset. We'll define crackJags array for i=0..20 with t and offset. Use to draw jagged edge. Opening width = 10+crack.length*0.25. Draw dark polygon with left/right edges. If not triggered, draw crack line with dashed.

Need opening visible and crack grows. crack.age increments after trigger; crack.length = crack.maxLength*easeOutCubic(min(crack.age/1.2,1)) maybe. If triggered, y fixed. Lower crack stronger.

Flow rate label update in HUD:
const flowBox = document.getElementById('flow'); flow.textContent = `${Math.round(flowRate)} units/s`; pressure etc.
waterline label: `${Math.round(tank.bottom-waterline)} px` or `${Math.round(waterDepth)} px`.
crack pressure: depth.

Need pointer events:
canvas.addEventListener('pointerdown', e => { const p=getCanvasPoint(e); if (!crack.triggered && is near crack) { crack.dragging=true; canvas.setPointerCapture(e.pointerId); crack.y=clamp(p.y, tank.y+55, tank.bottom-80); } });
pointermove if dragging update.
pointerup release.
getCanvasPoint: rect = canvas.getBoundingClientRect(); x=(clientX-rect.left)*canvas.width/rect.width; y=...
Need maybe use touch-action none in CSS.

Trigger:
document.getElementById('burst').onclick = () => triggerBurst();
reset button.
Maybe keyboard? no.

Need initial crack lower? If lower crack should stronger. Maybe set crack.y=350. User can drag. Need maybe default crack is "drag me".

Need "side panel develops a visible crack and then bursts": before trigger, crack visible. After trigger, burst.

Need "water escaping through the opening with flow strength based on water depth and decreasing as tank drains": flow formula and HUD.

Need "curved water jet affected by gravity": particles.

Need "spreading puddle collides with room boundaries": puddle particles.

Need "Fish, rocks, plants, and floating toy reacting differently according to density, buoyancy, drag, and current": code. Need maybe include legend? Could draw labels: Fish, Rock, Toy. Need maybe "Fish: density near neutral, swims against current; rock: high density; toy: buoyant; plant: anchored." But requirement maybe code. We can include labels in scene? Maybe draw small labels above objects? Fine.

Need "Objects transitioning correctly from underwater motion to airborne motion and then to floor collisions": implement and maybe draw fish/toy airborne. Need maybe fish after breach collides with floor.

Need "Fish attempting to swim against current before being swept through breach": fish behavior.

Need "Glass fragments with angular velocity, collisions, and water resistance": code.

Need "visible waterline lowers continuously rather than disappearing all at once": waterline.

Need maybe "large aquarium" use big.

Let's craft code. Need be careful with context variables.

Potential issue with code length. We can write full file.

Let's create with write.
write {}

TAKE IT TO CODE

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

RUN

modelnex-n2-mini
vendorNex AGI
harnesspi-agent
generated2026-08-02

METRICS

runtime600.0s
tokens in1.8k
tokens out4.6k
cached0
cost / run$0.0005