An SPH fluid simulation in one file: 2500-4000 particles, spatial hash, fixed timestep, metaballs and bloom, plus a self-test that prints a checksum. Fable 5 is the most expensive single run in this catalogue.
"Lumen Tank — fluid simulation" is one prompt run by 2 models: Fable 5 and Kimi K3.2 of 2 finished; 2 measured locally.
Models
2
Completed
2 of 2
Cheapest completed run
Fable 5 — $31.9
Measured locally in Bench Arena: tokens reconciled on the harness log, cost recomputed from them.
Output
Fable 5
Kimi K3
Prompt84 lines · EN
## 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.