oneshotlm
Compare all models on this prompt →

Fluid simulation · grok-4.20

Create a real-time fluid simulation on a full-screen canvas where dragging the mouse pushes swirling colored dye through the fluid.

⛨ sandboxed Open in new tab ↗
transcript · 6 messages
user

Create a real-time fluid simulation on a full-screen canvas where dragging the mouse pushes swirling colored dye through the fluid.

assistant
thinking
The task is: "Create a real-time fluid simulation on a full-screen canvas where dragging the mouse pushes swirling colored dye through the fluid." I must build a single-page web app in one self-contai...The task is to create a real-time fluid simulation on a full-screen canvas where dragging the mouse pushes swirling colored dye through the fluid. It must be a single self-contained index.html file with CSS in <style> and JS in <script>, no external resources.

I need to implement a 2D fluid simulation using 2D canvas or WebGL. For real-time performance, WebGL with shaders is standard.

I'll use WebGL for this.

First, the HTML structure:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Fluid Simulation</title>
<style>
body { margin:  0; overflow: hidden; background: #000; }
canvas { display: block; }
</style>
</head>
<body>
<canvas id="c"></canvas>
<script>
// JS code
</script>
</body>
</html>

For the JS, I'll set up WebGL, create shaders, handle 2D arrays for velocity and density using textures, ping-pong FBOs, mouse interaction by splatting color and velocity at mouse position.

I'll use a simplified version with Jacobi solver for pressure.

To keep it manageable, I'll use a known minimal code structure.

Let me define the code.

First, get canvas, 2D context no, WebGL2 for better.

Use webgl2.

Code:

const canvas = document.getElementById('c');
const gl = canvas.getContext('webgl2');
if (!gl) alert('WebGL2 not supported');

function resize() {
  canvas.width = window.innerWidth;
 canvas.height = window.innerHeight;
  gl.viewport(0, 0, canvas.width, canvas.height);
}
window.addEventListener('resize', resize);
resize();

Then, create program for quad.

const vertexShaderSource = `#version 300 es
in vec2 position;
void main() {
  gl_Position = vec4(position, 1.0);
}
`;

const fragmentShaderSource = `#version 300 es
precision highp float;
out vec4 color;
void main() {
  color = vec4(1.0);
}
`;

Then, compile shaders, link program, etc.

This will be long, 200+ lines.

I need to define all shaders: one for display, one for advection, one for divergence, one for pressure jacobi, one for gradient subtraction.

Also, create textures for velocity, density, etc.

Use ping pong by swapping indices.

For mouse, track mouse position, on mousedown/mousemove add splat.

For color, use different colors for dye, perhaps cycle hue.

For swirling, add curl to velocity.

Yes, standard.

To provide the answer, I need to output the complete index.html.

Since it's long, I'll generate it.

Let's write it.

I recall a complete code for this is available in my knowledge base.

Here's a compact version I can create.

Let me try to write a complete one.

First, the code:

const canvas = document.getElementById('c');
const gl = canvas.getContext('webgl2');
const ext = gl.getExtension('EXT_color_buffer_float');
if (!ext || !gl.getExtension('OES_texture_float_linear')) {
  alert('Required extensions not supported');
}

let program, quadBuffer;
let velocity, density, pressure, divergence;
let fboPingPong = [];

let mouse = {x:0, y:0, down:false};
let color = [ 0.5, 0.5, 0.5 ]; // base color

canvas.addEventListener('mousemove', e => {
  mouse.x = e.clientX / canvas.width;
  mouse.y = 1 - e.clientY / canvas.height;
  mouse.down = true;
});
canvas.addEventListener ('mousedown', e => {
  mouse.down = true;
});
canvas.addEventListener('mouseup', e => mouse.down = false);
canvas.addEventListener('mouseleave', e => mouse.down = false);

function loadShader(type, source) {
  const shader = gl.createShader(type);
  gl.shaderSource(shader, `#version 300 es\n${source}`);
  gl.compileShader(shader);
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    console.error(gl.getShaderInfoLog(shader));
  }
  return shader;
}

function createProgram(vs, fs) {
  const program = gl.createProgram();
 gl.attachShader(program, loadShader(gl.VERTEX_SHADER, vs));
  gl.attachShader(program, loadShader(gl.FRAGMENT_SHADER, fs));
  gl.linkProgram(program);
  if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
    console.error(gl.getProgramInfoLog(program));
  }
  return program;
}

 // Define all shaders

const vs = `
in vec2 a_position;
void main() {
  gl_Position = vec4(a_position, 1.0);
}
`;

const displayFs = `
precision highp float;
uniform sampler2D u_texture;
uniform vec2 u_resolution;
out vec 4 outColor;
void main() {
  vec2 uv = gl_FragCoord.xy / u_resolution;
  vec4 col = texture(u_texture, uv);
  outColor = col;
}
`;

 // Add color to make it colored, perhaps modify to use a color map.

To make it colored, the density is 3 channels, but let's use one density and color based on velocity angle or something.

For simplicity, let's use density as RGB.

Let's use velocity as RG, density as RGB in separate textures.

Let's follow a standard template.

Let's search my knowledge for a self-contained WebGL fluid simulation code.

One example is this:

 // I will write a full one.

To save time, perhaps use a particle based approach that is easier to write without errors.

Let's go with that.

Let me write the code for particle based fluid sim.

The code will be shorter.

Here is the plan:

- Canvas full screen

- GRID_SIZE = 64 // for velocity grid

- vel = new Float32Array(GRID_SIZE * GRID_SIZE * 2); // x and y interleaved or separate

- particles = [];

- for (let i=0; i<5000; i++) particles.push(new Particle());

- class Particle {

  constructor() {

    this .reset();

  }

  reset() {

    this.x = Math.random() * canvas.width;

    this.y = Math.random() * canvas.height;

    this.hue = Math.random() * 360;

  }

  update(dt) {

    // sample velocity

    let gx = this.x / canvas.width * (GRID_SIZE -1);

    let gy = this.y / canvas.height * (GRID_SIZE -1);

    let vx = sample(velX, gx, gy);

    let vy = sample(velY, gx, gy);

    this.x += vx * dt * 10;

    this.y += vy * dt * 10;

    // clamp and bounce

    if (this.x < 0) { this.x = 0; this.vx *= -1; }

    if (this.x > canvas.width) { this.x = canvas.width; this.vx *= -1; }

    same for y

  }

}

- Then updateField function that runs every frame:

  // copy vel to temp

  // for each cell average neighbors with damping

  // add mouse force

  // then copy back

- addForce function:

  let mx = mouse.x * canvas.width;

  let my = mouse .y * canvas.height;

  let gx = mx / canvas.width * (GRID_SIZE -1);

  let gy = my / canvas.height * (GRID_SIZE -1);

  let dx = mouse.dx * 0.5; // from last mouse

  let dy = mouse.dy * 0.5 ;

  for cells in radius add dx * weight to velX etc.

- In animate:

  updateField();

  for each particle update();

  render();

  requestAnimationFrame(animate);

- render:

  ctx.fillStyle = 'rgba(0,0,0,0.05)';

  ctx.
write index.html (16553 bytes)
toolResult

Successfully wrote 16553 bytes to index.html

assistant
thinking
The task is complete. I've created a single self-contained index.html file with a real-time fluid simulation. It uses a canvas that fills the screen, particle-based dye visualization with swirling col...
bash {"command":"ls -la"}
toolResult

total 20 drwxr-sr-x. 2 node node 24 Aug 1 20:00 . drwxrwsrwx. 3 root node 16 Aug 1 19:58 .. -rw-r--r--. 1 node node 16553 Aug 1 20:00 index.html

assistant
thinking
The file has been created successfully. It's a self-contained index.html with a real-time fluid simulation using particles that follow a velocity field (simplified Navier-Stokes inspired diffusion and...

The simulation has been built and saved to **index.html**. Open the file in a browser to interact with it (drag mouse or finger to push swirling colored dye; press **C** to clear or **R** to reset flow). It is completely self-contained.

TAKE IT TO CODE

A single self-contained HTML file — copy it and open in any browser, or reuse the prompt with your own pi-agent run.

RUN

modelgrok-4.20
vendorxAI · Grok
harnesspi-agent
generated2026-08-01

EVALUATION

3/5 working

Particle-based fluid sim with dye trails; drag creates a clear swirl push, though visuals look noisy/sparkly rather than smooth fluid.

Vision grade · anthropic/claude-sonnet-5

MEASUREMENTS

Raw readings from the capture, not a verdict — the grade above is the only judgement. Percentages are how much of the screen changed; the measure under-reports sparse motion on a dark background.

idle motion80.8%
on click43.3%
on drag34.7%
on wheel29.2%
on enter + space63.0%
on arrow keys54.5%
on w/a/s/d46.5%
frame spread57.2 / 255
console errors1
js errors none

METRICS

runtime93.1s
tokens in5.6k
tokens out13k
cached7.2k
cost / run$0.0416