## 1. THE FINAL PROMPT Build **LUMEN TANK** — a single-page, cinematic, fully deterministic 2D particle-fluid laboratory staged as a luxurious night-lab light installation. A glowing frosted-glass tank holds 2,500–4,000 mutually interacting liquid particles driven by real fluid math; light, caustics, bloom, and dye respond continuously to the fluid's motion. It must be spectacular within 2 seconds of opening — before any input — while remaining, underneath, a precise and reproducible scientific instrument. **OUTPUT FORMAT:** Output ONLY the complete contents of one self-contained `index.html` file. No commentary, no explanation, no additional files. ### HARD CONSTRAINTS - One inline `index.html`. Vanilla JS + Canvas 2D / WebGL / SVG / DOM+CSS only. Zero external libraries, frameworks, CDN links, fonts, images, audio, or network requests of any kind. No `fetch`, no module `import`, no external `script src`, `link href`, `img`, `video`, or `iframe`. No audio whatsoever. - Must run by double-clicking the file (`file://`) in a current browser, with zero console errors or warnings through 60 seconds of heavy randomized interaction. - Target 60 FPS at 1280×720 with ≥2,500 live particles on a 2021-or-newer mid-range laptop with hardware acceleration enabled. Acceptance floor: ≥55 FPS sustained for at least one contiguous 10-second window after up to 5 seconds of warmup/adaptation, measured by a real rolling FPS meter computed from actual frame times. - No placeholders, TODOs, lorem ipsum, dead controls, or stub features. Every visible control must function. - No menu, click-to-start gate, or loading screen: the simulation and attract mode are already running on first paint. ### CORE SIMULATION (the one deep system — do it properly) - 2,500–4,000 fluid particles with genuine SPH-style (or equivalent particle-fluid, e.g. PBF) dynamics: hand-written kernel functions (poly6/spiky/viscosity or equivalent), kernel-weighted density, pressure, viscosity, and surface-tension/cohesion forces. No fake curl-noise advection masquerading as fluid. - Neighbor search via spatial hash or uniform grid — strictly no O(n²) all-pairs loop. - Fixed timestep of 1/120 s with an accumulator, a per-frame step cap, and render interpolation between the last two physics states. Simulation speed must be decoupled from display rate: under CPU or frame throttling that reduces display rate to roughly 20–40 FPS, the fluid must evolve at the same wall-clock speed, and the HUD physics-step counter must show ≈120 steps per wall second, ±10%. - Typed-array particle storage and preallocated buffers; zero per-frame allocations in the hot loop; object pooling for any transient effects. - Tank walls plus ≥3 solid obstacles (at least one grabbable and rotatable) with collision and friction against the fluid. - Stability: velocity and density clamps plus a NaN guard that counts and neutralizes any corrupted particle. The HUD NaN counter must read 0 after 30 seconds of maximal violent stirring with all dyes active and gravity cycling; the fluid must visibly splash, form droplets and menisci, and pool calmly at rest. - Determinism: all randomness from a seeded PRNG (mulberry32/splitmix or equivalent); no `Math.random()` or `Date.now()` anywhere in simulation or render state. Wall-clock time (rAF timestamp or `performance.now()`) may drive only the accumulator's step scheduling and HUD/FPS timing; the fixed 1/120 s integration step, all physics state, and all render/visual state must derive solely from the deterministic step counter — never from elapsed wall time. Positions folded into checksums only after rounding, so runs reproduce exactly. ### CINEMATIC RENDERING - Art direction: deep indigo/charcoal night-lab palette with one warm amber accent; frosted-glass tank with rim highlights and refraction feel; reflective floor; volumetric-feeling key light; rule-of-thirds composition; subtle camera drift of a few pixels; minimal elegant HUD in system font stacks. - The fluid must render as a smooth continuous surface — density splatting to an offscreen field with soft threshold shading, screen-space metaballs, or an equivalent technique — never as bare dots. Velocity-stretched sprites or short trails are allowed as an accent layer only. - Three dye channels (cyan, magenta, amber) that advect with the fluid and mix believably where they overlap. - Post-processing chain: bloom, tone mapping, vignette, film grain, and a caustic light pattern on the tank floor whose intensity is driven by measured fluid-agitation energy within the same frame. - Preferred implementation is hand-written WebGL/GLSL for the fluid surface and post chain. However, a Canvas 2D fallback renderer must automatically engage if WebGL is unavailable (with a small HUD notice). The fallback must render the full interactive scene — tank, smooth fluid surface, dyes, obstacles, caustics, and HUD — but may simplify bloom, grain, and other post effects. A blank or black page is a failure. - One shared per-frame state store: agitation energy, FPS, and pause state are computed once per frame and drive caustics, bloom strength, HUD, and attract-mode camera drift. The coupling must be real — agitating the fluid visibly intensifies the caustics within about 0.5 seconds, and at rest they settle to baseline within about 2 seconds. - Film grain and all time-based effects must be pure functions of a deterministic frame counter so that a frozen frame is pixel-stable. ### ATTRACT MODE & EXPLICIT STATE MACHINE - Exactly these states, shown live in the HUD: `ATTRACT → LIVE ⇄ PAUSED`, with a single-step sub-state while paused. `R` resets and returns to `ATTRACT`. The idle timer runs only in `LIVE`; after ≥8 seconds without input in `LIVE`, the app returns to `ATTRACT`. `PAUSED` never auto-transitions. - `ATTRACT`: a scripted, deterministic auto-choreography (a virtual stirrer plus timed dye injections, all from the seeded PRNG script) that is already running on first paint and is visually spectacular within 2 seconds — glowing liquid swirling, dyes mixing, caustics pulsing. Any key press, mouse button, wheel event, or drag transitions to `LIVE` within one frame; pointer movement alone does not. Space from `ATTRACT` transitions to `LIVE` unpaused; the next Space pauses. - `PAUSED` (Space): physics and all animation, including film grain, freeze completely. The deterministic frame counter and every HUD readout — including FPS, steps/second, and checksum — freeze at their paused values; no pixel may change until `S` or another key is pressed (the brush cursor also freezes while paused). Two screenshots taken 5 seconds apart while paused must be pixel-identical. - Single-step (S, only while paused): advances exactly one 1/120 s physics step per keypress and re-renders exactly once; the HUD total-step counter increments by exactly 1 and the rendered frame changes. ### INTERACTION & JUICE - Left-drag: vortex stir with velocity-scaled force. Right-drag: inject a continuous stream of the current dye. Right-click: inject a burst. Mouse wheel: brush radius. A circular brush cursor always shows the current stir/inject radius. The context menu must be suppressed inside the tank. - Keys: Space pause/resume; S single-step; R deterministic reset (same seed → identical scene); G cycle gravity down/zero/up; 1/2/3 select dye; O or click grabs the obstacle nearest the cursor, drag moves it, Q/E rotate it; V cycles internal render scale 100%/75%/50%; H toggle HUD; T run the built-in self-test. The grabbed obstacle must be visibly highlighted. - Juice: splash bursts on hard impacts, expanding ripple rings on dye injection, subtle screen shake scaled to impact energy, and smooth eased transitions everywhere — all derived from the deterministic state, never breaking reproducibility. ### HUD, SELF-TESTS & PERSISTENCE - HUD (toggle with H, visible by default on first load) shows: current state-machine state, real rolling FPS, live particle count, physics steps in the last second, total physics-step counter, max particle speed and its clamp threshold, NaN counter, current seed, current dye, gravity mode, brush radius, render scale, and a 6-hex-digit state checksum (FNV-1a-style fold of rounded particle positions). - Pressing T runs the built-in deterministic self-test: first restore the canonical configuration (seed, dye, obstacle layout, quality), ignoring all persisted settings; then replay a fixed scripted input sequence for exactly 600 physics steps, with no further user input; then display the resulting checksum and return to `ATTRACT`. The replay may fast-forward. This checksum must be identical across page reloads. - localStorage persistence with a try/catch safe fallback: last selected dye, HUD visibility, and render quality persist across reloads and degrade to a safe no-op when storage is blocked. - Correct resize and devicePixelRatio handling (crisp at DPR 2, undistorted at 900×600); pause cleanly on tab visibility change and resume without a time jump. - Adaptive internal render scale is permitted only after FPS < 50 sustained for >2 seconds, and may reduce resolution only — never remove particles, obstacles, dyes, or post effects. ### ACCEPTANCE CRITERIA (all must hold) 1. Opens via `file://` double-click; the network panel shows zero non-file requests; the console stays clean through 60 s of heavy randomized input. 2. With zero input, screenshots at t=1 s and t=2.5 s differ clearly in fluid/dye configuration, and the t=2.5 s frame shows a fully lit scene with a continuous fluid surface (not bare dots), ≥2 dye colors mixing, and no blank, loading, or setup UI. 3. After up to 5 s warmup, the built-in meter sustains ≥55 FPS for a contiguous 10 s window at 1280×720 with ≥2,500 live particles. 4. Determinism: run T, note the checksum; reload; run T again → identical checksum, repeatable across 3 reloads. 5. Stability: after 30 s of maximal stirring with all dyes and gravity cycling, NaN counter = 0, max speed remains finite and below the displayed clamp threshold, and the fluid pools at rest. 6. Space pauses physics, all animation including grain, and every HUD readout; two screenshots 5 s apart while paused are pixel-identical; each S press advances the total-step counter by exactly 1 and changes the rendered frame. 7. State machine correct and HUD-visible with HUD shown: `ATTRACT` on load; any key press, mouse button, wheel event, or drag → `LIVE` within one frame (pointer movement alone does not); 8 s idle in `LIVE` → `ATTRACT`; R → reset + `ATTRACT`; the state label updates in the same frame as each transition. 8. Throttle test: under DevTools 6× CPU throttling or an equivalent condition reducing display rate to roughly 20–40 FPS, the physics-step counter still advances ≈120 steps per wall second (±10%) — fluid motion does not slow down. 9. Every control produces a visible effect within one frame (stir, dye inject, wheel radius ring, gravity cycle readout, obstacle grab highlight/rotation, dye-select readout, render-scale readout, HUD toggle); dye choice, HUD visibility, and render quality persist across reload. 10. Coupling and fallback: vigorous stirring visibly intensifies the caustic floor light and bloom within about 0.5 s and both settle to baseline within about 2 s after stirring stops; with WebGL disabled, the fallback renders the complete tank scene with a HUD notice — never a black page. Output only the `index.html` file content. ## 2. FIVE-LINE RATIONALE Real SPH kernels, spatial hashing, fixed-timestep accumulation, and typed-array pooling are genuine hard-core CS that cannot be faked with a pretty flow field — weak models emit O(n²) loops that collapse below the FPS floor or slow-motion accumulators that the throttle test instantly exposes. The determinism contract (seeded PRNG plus reload-identical 600-step checksum) is the single most objective, hardest-to-fake check in the set, smoking out hidden `Math.random`/`Date.now` use, uninitialized memory, and iteration-order nondeterminism. Demanding a cinematic metaball surface, dye mixing, bloom, and agitation-driven caustics — coupled to live physics within one frame — forces real shader/post-processing and state-store discipline; weaker models deliver flat dot clouds with cosmetic glow and fake coupling. The input-free scripted attract choreography, HUD-visible state machine with pause and single-step sub-state, one-frame input takeover, and localStorage persistence multiply the integration surface, exposing dead keys, broken transitions, setup gates, and storage crashes that single-scene demos never hit. The pixel-identical freeze test, NaN counter, and exact single-step counter turn stability and render determinism into binary pass/fail evidence, while the mandated Canvas fallback preserves WebGL ambition without the hardware-fragile black-screen lottery of absolute raymarching requirements. ## 3. SCORING RUBRIC (10 POINTS) 1. **Clean load (1 pt):** Page opens via `file://` double-click; network panel shows zero non-file requests; console shows zero errors/warnings after 60 s of randomized clicking, dragging, and keypresses. 2. **Two-second wow (1 pt):** With zero input, screenshots at t=1 s and t=2.5 s differ clearly in the tank region; the t=2.5 s frame shows a fully lit scene, a continuous fluid surface (not dots), ≥2 perceptually distinct dye hues mixing, and no blank, loading, or gate UI. 3. **Performance (1 pt):** After up to 5 s warmup, the built-in rolling FPS meter reads ≥55 sustained for one contiguous 10 s window at 1280×720 while HUD particle count shows ≥2,500 live particles; an external frame-time cross-check must agree within 10%. 4. **Determinism (1 pt):** Press T, record the 6-hex checksum; reload the page; press T again → identical checksum; holds across 3 consecutive reloads. 5. **Stability (1 pt):** After 30 s of maximal stirring with all three dyes injected and gravity cycled, HUD NaN counter = 0, max speed stays finite and below the displayed clamp threshold, and the fluid visibly pools flat when stirring stops. 6. **Freeze & step (1 pt):** Press Space; all HUD values freeze and two screenshots taken 5 s apart are pixel-identical (zero diff); each press of S increments the HUD total-step counter by exactly 1 and the rendered frame changes. 7. **State machine (1 pt):** With HUD visible, it shows `ATTRACT` on load; any key press, mouse button, wheel event, or drag switches the label to `LIVE` within one frame while pointer movement alone does not; after 8 s idle in `LIVE` it returns to `ATTRACT`; R resets and restores `ATTRACT` with the label updating the same frame. 8. **Throttle independence (1 pt):** Under DevTools 6× CPU throttling or an equivalent condition reducing display rate to roughly 20–40 FPS, the HUD physics-steps/second readout remains ≈120 ±10%, and fluid motion visibly keeps wall-clock speed rather than halving. 9. **Controls & persistence (1 pt):** Stir, dye injection, wheel radius ring, G gravity readout, O/Q/E obstacle highlight and rotation, 1/2/3 dye readout, V render-scale readout, and H HUD toggle each produce a visible effect within one frame; selected dye, HUD visibility, and render quality survive a reload. 10. **Coupling & fallback (1 pt):** Vigorous stirring visibly intensifies the floor caustics and bloom within about 0.5 s and both settle to baseline within about 2 s after stirring stops; with WebGL disabled, the fallback renders the full tank scene with a HUD notice instead of a black page.