OverviewPhysics Lab › R28
Physics Lab

R28 · Particle fountain

Archive round — this round predates the blind-first layout, so answers appear with model names attached. Newer rounds keep the models anonymous until you reveal them.
scored 12/13 models answered
Show task
Create ONE self-contained HTML file (STRICT LIMIT: max 120 lines total) that shows a particle fountain on a <canvas>: exactly 200 particles continuously emitted upward from the bottom center with slight random spread, affected by constant gravity and by horizontal wind controlled with an HTML range slider (negative to positive wind, live effect). Particles bounce off the floor with damped velocity and respawn at the emitter when their energy is spent. Smooth requestAnimationFrame animation. No external libraries, no comments needed. Output ONLY the HTML file content, nothing else.

Answers

Local (Brain cluster)
gemma4:26b267.9 sOK★ 8.5/10
Show code
<!DOCTYPE html>
<html style="margin:0;padding:0;overflow:hidden">
<body style="margin:0;padding:0;background:#000;overflow:hidden">
<input type="range" id="w" min="-0.5" max="0.5" step="0.01" style="position:fixed;top:10px;left:10px;z-index:10">
<canvas id="c"></canvas>
<script>
const canvas = document.getElementById('c'), ctx = canvas.getContext('2d'), slider = document.getElementById('w');
let width, height, particles = [], wind = 0;
const count = 200;
slider.oninput = (e) => wind = parseFloat(e.target.value);
function reset(p) {
  p.x = width / 2;
  p.y = height;
  p.vx = (Math.random() - 0.5) * 4;
  p.vy = -Math.random() * 8 - 5;
  p.life = Math.random() * 100 + 50;
  p.color = `hsl(${Math.random() * 360}, 70%, 60%)`;
}
function init() {
  width = canvas.width = window.innerWidth;
  height = canvas.height = window.innerHeight;
  particles = [];
  for (let i = 0; i < count; i++) {
    particles[i] = {};
    reset(particles[i]);
  }
}
function animate() {
  ctx.fillStyle = 'rgba(0, 0, 0, 0.15)';
  ctx.fillRect(0, 0, width, height);
  for (let i = 0; i < count; i++) {
    let p = particles[i];
    p.vy += 0.15;
    p.vx += wind;
    p.x += p.vx;
    p.y += p.vy;
    p.life -= 0.6;
    if (p.y > height) {
      p.y = height;
      p.vy *= -0.6;
    }
    if (p.x < 0) p.x = width;
    if (p.x > width) p.x = 0;
    if (p.life <= 0) reset(p);
    ctx.fillStyle = p.color;
    ctx.beginPath();
    ctx.arc(p.x, p.y, 3, 0, 6.28);
    ctx.fill();
  }
  requestAnimationFrame(animate);
}
window.onresize = init;
init();
animate();
</script>
</body>
</html>
qwen3-coder:30b20.7 sSTAT★ 3/10
Show code
```html
<!DOCTYPE html>
<html>
<head>
<title>Particle Fountain Simulation</title>
<style>
body { margin: 0; display: flex; justify-content: center; align-items: flex-end; height: 100vh; background-color: #1a1a1a; }
canvas { border: 1px solid #333; background-color: #000; }
#controls { position: absolute; bottom: 5%; width: 90%; text-align: center; padding: 10px; background-color: rgba(0,0,0,0.7); color: white; z-index: 10; }
</style>
</head>
<body>

<canvas id="particleCanvas"></canvas>
<div id="controls">Wind Strength: <input type="range" id="windSlider" min="-0.5" max="0.5" step="0.01" value="0"></div>

<script>
const canvas = document.getElementById('particleCanvas');
const ctx = canvas.getContext('2d');
let W, H;

canvas.width = window.innerWidth * 0.8 > 600 ? window.innerWidth * 0.8 : 600;
canvas.height = window.innerHeight * 0.7 > 400 ? window.innerHeight * 0.7 : 400;

window.addEventListener('resize', () => {
    W = canvas.width = window.innerWidth * 0.8 > 600 ? window.innerWidth * 0.8 : 600;
    H = canvas.height = window.innerHeight * 0.7 > 400 ? window.innerHeight * 0.7 : 400;
});

let GRAVITY = 0.1;
const NUM_PARTICLES = 200;
const PARTICLES = [];
const floorY = H * 0.95;
let WIND_STRENGTH = 0;

function Particle(x, y) {
    this.x = x + (Math.random() - 0.5) * 15;
    this.y = y;
    this.vx = Math.random() * 2 - 1;
    this.vy = Math.random() * 3 + 3; 
    this.radius = Math.random() * 1.5 + 0.5;
    this.color = `hsl(${(Math.random() * 360)}, 80%, 70%)`;

    this.update = function() {
        this.vy += GRAVITY;
        this.vx += (WIND_STRENGTH * 0.01);
        this.x += this.vx;
        this.y += this.vy;

        if (this.y + this.radius >= floorY && this.vy > 0) {
            this.y = floorY - this.radius;
            this.vy *= -0.65;
            this.vx *= 0.98;
        }
    };

    this.draw = function() {
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
        ctx.fillStyle = this.color;
        ctx.fill();
    };

    this.dead = function() {
        return (Math.abs(this.vy) < 0.5 && Math.random() < 0.01);
    }
}

for (let i = 0; i < NUM_PARTICLES; i++) {
    PARTICLES.push(new Particle(W / 2, H));
}

const windSlider = document.getElementById('windSlider');
windSlider.addEventListener('input', function() { WIND_STRENGTH = parseFloat(this.value); });


function animate() {
    ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
    ctx.fillRect(0, 0, W, H);

    for (let i = 0; i < NUM_PARTICLES; i++) {
        const p = PARTICLES[i];
        p.update();
        p.draw();
        if (p.dead()) {
            p.y = H; // Respawn at top center
            p.vx = Math.random() * 2 - 1;
            p.vy = Math.random() * 3 + 3;
            p.color = `hsl(${(Math.random() * 360)}, 80%, 70%)`;
        }
    }

    requestAnimationFrame(animate);
}

animate();
</script>

</body>
</html>
```
qwen3.5:9b29.5 sERRSTATLIM★ 1.5/10
Show code
```html
<!DOCTYPE html>
<html>
<head>
    <title>Particle Fountain</title>
    <style>
        body { margin: 0; overflow: hidden; background-color: #111; }
        #canvas { display: block; border-bottom: 5px solid #aaa; }
        #controls { position: absolute; top: 20px; left: 50%; transform: translateX(-50%); z-index: 10; color: white; text-align: center;}
    </style>
</head>
<body>

<canvas id="canvas"></canvas>
<div id="controls">
    Wind Control (W): <span id="windValue">0</span><br>
    <input type="range" id="windSlider" min="-1.5" max="1.5" step="0.05" value="0">
</div>

<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let width = window.innerWidth;
let height = window.innerHeight * 0.9; // Reserve space for controls/floor visual
canvas.width = width;
canvas.height = height;

window.onresize = function() {
    width = window.innerWidth;
    height = window.innerHeight * 0.9;
    canvas.width = width;
    canvas.height = height;
};

const floorY = height - 10;
let particles = [];
const GRAVITY = 0.5;
const DAMPING = 0.75; // Bounce coefficient
let windForce = 0;

document.getElementById('windSlider').addEventListener('input', (e) => {
    windForce = parseFloat(e.target.value);
    document.getElementById('windValue').textContent = windForce.toFixed(2);
});

const EMITTER_X = width / 2;
const emitterLifetime = 300; // Frames until particle resets/re-emits

function spawnParticle() {
    particles.push({
        x: EMITTER_X + (Math.random() - 0.5) * 20,
        y: floorY - 10,
        vx: (Math.random() - 0.5) * 1.5,
        vy: -(3 + Math.random() * 2), // Strong upward starting velocity
        lifetime: emitterLifetime,
        radius: 2 + Math.random() * 1,
        color: `hsl(${Math.random() * 360}, 80%, 70%)`
    });
}

function updateParticles() {
    for (let i = particles.length - 1; i >= 0; i--) {
        let p = particles[i];

        p.vx += windForce * 0.05;
        p.vy += GRAVITY * 0.08; // Gravity application

        p.x += p.vx;
        p.y += p.vy;
        p.lifetime--;

        // Collision Detection and Bounce
        if (p.y + p.radius > floorY) {
            p.y = floorY - p.radius;
            p.vy *= -DAMPING; 
            p.vx *= DAMPING * 0.95; // Dampen horizontal speed on bounce
            // If vertical velocity is too low, it's essentially resting/spent
            if (Math.abs(p.vy) < 2 && p.lifetime < emitterLifetime / 4) {
                p.y = floorY - p.radius * Math.random()*0.1; // Scatter slightly when "rebooting"
                p.vy = -(1 - Math.random() * 0.5); // Give it a low restart boost
            }

        } else if (Math.abs(p.vx) > width * 2 || p.y < -30) {
             // Out of bounds or too far up, remove the particle and respawn it below to continue fountain effect
             particles[i] = null; 
             if (p.lifetime < emitterLifetime / 4 && Math.random() > 0.5) {
                 Object.assign(p, { x: EMITTER_X + (Math.random() - 0.5) * 10, y: floorY - 2, vx: (Math.random()-0.5).*1, vy: -(3+Math.random()*2), lifetime: emitterLifetime});
             } else {
                 i--; continue; // Skip drawing/updating if it was reset to null
             }
        }

        if (!particles[i]) {
            particles.splice(i, 1);
            continue;
        }
    }

    // Filter out dead particles and re-emit some
    for (let i = 0; i < particles.length; i++) {
        let p = particles[i];
        if (p) p.lifetime--;

        if (p.lifetime <= 0 || Math.random() < 0.02) { // Random chances to emit new particles and respawn old ones
            spawnParticle();
        }
    }
}

function drawParticles() {
    for (let i = 0; i < particles.length; i++) {
        let p = particles[i];
         if (!p) continue; // Skip null particles from filtering above
        ctx.beginPath();
        ctx.arc(p.x, p.y, p.radius * (1 - Math.min(0.5, 1/p.lifetime)), 0, Math.PI * 2); // Fade effect based on lifetime
        ctx.fillStyle = p.color;
        ctx.fill();
    }
}

function update() {
    // Clear canvas and draw a gradient background for trails
    ctx.globalAlpha = 0.15;
    ctx.fillStyle = '#000';
    ctx.fillRect(0, 0, width, height);
    ctx.globalAlpha = 1;

    updateParticles();
    drawParticles();

    requestAnimationFrame(update);
}

// Initialization run
setInterval(() => spawnParticle(), 80); // Continuous emitter spawn
update();
</script>

</body>
</html>
```
qwen3:14b17.2 sOK★ 8.5/10
Show code
<!DOCTYPE html>
<html>
<body><canvas id="c"></canvas><input type="range" id="w" min="-0.5" max="0.5" step="0.01" value="0.0">
<script>
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
let width, height;

function resize() {
    width = window.innerWidth * 0.8;
    height = window.innerHeight * 0.6;
    canvas.width = width;
    canvas.height = height;
}
window.addEventListener('resize', resize);
resize();

const particles = [];
const numParticles = 200;
const emitCount = 5; // Particles emitted per frame refresh cycle (for continuous feel)
let emitterX, emitterY;

// Physics constants
const GRAVITY = 0.1;
const DAMPING = 0.6;

function Particle(x, y, vx, vy) {
    this.x = x;
    this.y = y;
    this.vx = vx;
    this.vy = vy;
}

Particle.prototype.update = function(wind) {
    this.vy += GRAVITY;
    this.vx = (this.vx * 0.98) + (wind * 0.1); // Drag/Wind influence
    this.x += this.vx;
    this.y += this.vy;

    if (this.y + 3 > height - 5 && this.vy > 0) {
        this.vy *= -DAMPING;
        this.vx *= DAMPING;
        this.y = height - 5; // Keep it on the floor
    }

    if (this.y + 10 > height * 2) {
        this.reset(Math.random() * width * 0.3 + width * 0.35, height - 5);
    }
};

Particle.prototype.reset = function(x, y) {
    this.x = x;
    this.y = y;
    // Initial upward velocity with slight random spread
    this.vy = -Math.random() * Math.max(2, height / 10); 
    this.vx = (Math.random() - 0.5) * width * 0.01;
};

function initParticles() {
    emitterX = width / 2;
    emitterY = height - 5;
    for (let i = 0; i < numParticles; i++) {
        const p = new Particle(emitterX, emitterY, Math.random() * 0.1 - 0.05, 2 + Math.random());
        particles.push(p);
    }
}

function animate() {
    ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
    ctx.fillRect(0, 0, width, height);

    const windRange = document.getElementById('w');
    const wind = parseFloat(windRange.value);

    particles.forEach((p, i) => {
        if (i < numParticles * 0.95 && Math.random() > 0.8) {
            // Simulate occasional emission/respawn for a continuous fountain effect
            if (p.y >= height - 20) {
                p.reset(Math.random() * width * 0.3 + width * 0.35, height - 5);
            }
        }

        p.update(wind);
    });

    ctx.fillStyle = 'rgba(255, 255, 255, 1)';
    particles.forEach(p => {
        ctx.beginPath();
        ctx.arc(p.x, p.y, 1.5, 0, Math.PI * 2);
        ctx.fill();
    });

    requestAnimationFrame(animate);
}

initParticles();
animate();
</script>
</body></html>
qwen3:8b DNF / pending DNF (infra)
DNF — infrastructure: 10/10 requests returned status 500
llama3.118.2 sERRSTATLIM★ 1.5/10
Show code
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title></title>
<style>
body {
  padding: 0;
  margin: 0;
}
canvas {
  border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="c"></canvas>
<input type="range" id="s" min="-10" max="10" value="5" />
<script>
const c = document.getElementById('c');
const s = document.querySelector('#s');
let p, g = 0.2 * Math.PI / 180, w;

function f(x, y) {
  return x + Math.random() / 100;
}

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

document.addEventListener(
  'DOMContentLoaded',
  function() {
    requestAnimationFrame(t);
    p = [];
    for (let i = 0; i < 200; i++) {
      const vx = f(Math.sin(Date.now() + i) * 2, Math.cos(Date.now()));
      const vy = -1;
      const x = canvas.width / 2,
        y = canvas.height;
      p[i] = { x, y, vx, vy, r: Math.random() * 3 };
    }
  },
  false
);

s.addEventListener('input', function() {
  w = s.value;
});

function t(_t) {
  let e;
  for (let i =
      0,
          l = p.length;
      i < l; ++i) {
        const x = p[i].x += p[i].vx,
          y = p[i].y += p[i].vy;
    if (
            w &&
            -w <= (canvas.width / 2 * Math.random() + Math.cos(Date.now())) &&
            (canvas.width / 2 * Math.random() + Math.cos(Date.now)) <=
              w
        ) {
          e = f(Math.sin(Date.now()) * 10, w);
          p[i].vx =
            -8 *
                (1 - Math.tan(w) * 0.05)
            + 2 * e;
          if (+p[i].y > y / 100) {
            for (
                  i = l > 1 ? l - 1 : 0,
                      s = false,
  p = [];
                ~s) {
      c.getContext("2d");
      c.fillStyle =
        typeof String.fromCharCode !== 'string' ||
          !/\n/.test(String.fromCharCode) ?
        "rgba(221,135,49,0.9)" :
        "#FFA07A";
    for (let i in p) {
            c.beginPath();
            c.arc(p[i].x, p[i].y += s ? -Math.abs(p[i].vy++ * 100 + 40) : Math.abs(-p[i].vy-- * 100 + 120), p[i].r,
                          0, 2 *
                            Math.PI);
            c.fill();
          }
      s ^= true;
    }
    for (; i < l; ++i) {
      e =
        (1 - p[i].y / y)
        * 5
        * f(Math.sin(Date.now()), w || -10),
            x = canvas.width / 2,
          y += s ? -Math.abs(p[
                v =
                  i != l - 1 ? i + 1 : 0
               ].vy++ * 100 + 40) : Math.abs(-p[
            l > 2 && v) {
              vx = w ? w / (1 +
                                p[i].y / y)
                              *
                                f(
                                    e =
                                      s || -w < i != l- 1 ? (i + 1) :
                                         0,
                                    Math.sin(Date.now() * 5),
                                    i == 3) - g : e = w / 16;
              vx *= 0.97; vy *= 0.98;
              p[i].x -= s || -w < l > i ? vx + x : x;
      }
    for (i = l - 1 ; ~i;) {
            c.fillStyle =
      "rgba(169, 169, 169,
        y / 100 * (
          g =
            typeof String.fromCharCode !== 'string' ||
            !/\n/.test(String.fromCharCode) ? 0.5 :
            "#FFFFFF";
    c.fillRect(p[i].x, Math.floor(
              p[i].y += s ? -vy++ * 10 : vy-- * 10), 5, 3);
      if (+p[
                    i].
                  y >
        l < 9 || g <= (Date.now%1.6e4).toString().
                          charCodeAt(0) % 32)
          p.pop(); else ++i;
    }
  }(g / e), f(e * Math.cos(Date.now()), w *
        (-g - (
            24 >
              y / 100 ? g = 10 :
                (
                  g =
1 + (e / y + (- Date.
             toLocaleString()),
                           '0'.charCodeAt(
                             i == e ||
                              e = p[
                                      l-1].y
                                >= c.height,
                                s ? -w <= l > i :
                                    s || w<=l>i?'':1),
                               18)));
          vy += g * 400;
      }
    if (p[i].r += s) {
        y -= p[
            ~i++ && p.length
                < 8 ?
            vy &&
              y >
                l <
                  4 ?
(l =
            i,
              Vy =

p[--l].y ) :
(c.getContext(
                          '2d'));c.fillStyle ='#666';
    for (; l < 9; ++l) {
      c.fillRect(p[l].x, Math.floor(p[l].vy *= -0.02 * p[l].r + 1)-40,
                                5);
        Vy = ((+Vy *=
                    p[
                       i =
                         g <= l && ~i ? l - 1 : -1]
                      .
                  vy));
    }
}
  }
);

requestAnimationFrame(t);
</script>
</body>
</html>
```
deepseek-r124.9 sERRSTAT★ 2.5/10
Show code
```html
<!DOCTYPE html>
<html><body style="margin:0;display:flex;flex-direction:column-align-items-center">
<canvas id=c width=800 height=600></canvas>
<div style="margin-top:20px">
<div style="display:inline-block;margin-right:10px">Wind speed: <span id="windValue" style="color:red">0</span> m/s</div>
<input type="range" id="windControl" min="-5" max="5" value="0" step="any">
</div>
<script>
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
let particles = [];
const emitterX = canvas.width / 2;
const emitterY = canvas.height;

class Particle{
    constructor(){
        this.x = emitterX + Math.random() * 5 - 2.5;
        this.y = emitterY;
        this.radius = Math.random() * 3 + 2;
        this.vx = 0;
        this.vy = -10;
        this.ang = (Math.random() - 0.5) * 0.3; 
    }
}

let wind = 0;
const gravity = 0.3;

function init(){
    for(let i=0;i<200;i++) particles.push(new Particle());
}
document.getElementById('windControl').addEventListener('input', e => {
    wind = parseFloat(e.target.value);
    document.getElementById('windValue').textContent = e.target.value;
});

function updateParticles(){
    particles.forEach(particle => {
        if(!particle) return;

        particle.vy += gravity;
        
        const vx = (Math.abs(wind) * Math.cos((window.mouseX - emitterX)/10)) || wind;
        particle.vx += vx * 2;
        
        let dx = particle.vx *= 0.98;
        let dy = (particle.vy *= 0.98);

        const res = canvas.getClientRect();
        if(particle.y + particle.radius > res.bottom){
            particle.vx *= 0.5;
            particle.vy *= -0.7;
            if(Math.abs(particle.vx) < 0.1 && Math.abs(particle.vy) < 0.1){
                particle.x = emitterX;
                particle.y = emitterY;
                particle.vx = 0;
                particle.vy = -10 + (Math.random() * 0.3 - 0.15);
            }
        }

        particle.x += dx;
        particle.y += dy;

        if(particle.x < 0 || particle.x > canvas.width) particle.vx *= -0.8;

    }).filter(p => {
        return p.x >=0 && p.x <= canvas.width && p.y <= canvas.height 
            || (p.vy && Math.abs(p.vx) + Math.abs(p.vy) > 0.1);
    });
}

function animate(){
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    
    particles.forEach(
        p => {
            if (!p) return;
            ctx.beginPath();
            ctx.arc(p.x, p.y, p.radius, 0, 2 * Math.PI);
            ctx.fillStyle = '#fff';
            ctx.fill();
        }
    );

    updateParticles();
    requestAnimationFrame(animate);
}

init();
animate();
</script>
</body></html>
```
mistral-small79.1 sSTAT★ 3/10
Show code
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Particle Fountain</title>
<style>
body { margin: 0; overflow: hidden; }
#controls { position: absolute; top: 10px; left: 10px; z-index: 1; }
canvas { display: block; }
</style>
</head>
<body>
<div id="controls">
    Wind: <input type="range" id="wind" min="-5" max="5" step="0.1" value="0">
</div>
<canvas id="canvas"></canvas>

<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const particles = [];
const windInput = document.getElementById('wind');

class Particle {
    constructor(x, y) {
        this.x = x;
        this.y = y;
        this.vx = (Math.random() - 0.5);
        this.vy = -(2 + Math.random());
        this.radius = 2;
    }

    update(Wind){
        this.vx += Wind/100;
        this.vy += 0.1;
        this.x += this.vx;
        this.y += this.vy;

        if (this.y > canvas.height - this.radius) {
            this.y = canvas.height - this.radius;
            this.vy *= -0.6;
            if (Math.abs(this.vy) < 0.1) {
                this.respawn();
            }
        }

        if (this.x < this.radius || this.x > canvas.width - this.radius) {
            this.vx *= -1;
            this.x = Math.min(Math.max(this.x, this.radius), canvas.width - this.radius);
        }
    }

    draw() {
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
        ctx.fillStyle = 'white';
        ctx.fill();
    }

    respawn() {
        this.x = canvas.width / 2;
        this.y = canvas.height - 10;
        this.vx = (Math.random() - 0.5) * 2;
        this.vy = -(2 + Math.random());
    }
}

for (let i = 0; i < 200; i++) {
    particles.push(new Particle(canvas.width / 2, canvas.height - 10));
}

function animate() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    const wind = parseFloat(windInput.value);
    for (let particle of particles) {
        particle.update(wind);
        particle.draw();
    }
    requestAnimationFrame(animate);
}
requestAnimationFrame(animate);

</script>
</body>
</html>
```
Cloud
GPT-5.5 (codex)16.3 sOK★ 8.5/10
Show code
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Particle Fountain</title>
<style>
html,body{margin:0;height:100%;overflow:hidden;background:#10151f;color:white;font-family:system-ui}
canvas{display:block;width:100vw;height:100vh}
#ui{position:fixed;left:16px;top:16px;background:#0008;padding:10px 12px;border-radius:8px}
input{vertical-align:middle}
</style>
</head>
<body>
<canvas id="c"></canvas>
<div id="ui">Wind <input id="wind" type="range" min="-0.25" max="0.25" step="0.01" value="0"></div>
<script>
const c=document.getElementById("c"),x=c.getContext("2d"),wind=document.getElementById("wind");
let W,H,DPR,ps=[];
function size(){
  DPR=devicePixelRatio||1; W=innerWidth; H=innerHeight;
  c.width=W*DPR; c.height=H*DPR; x.setTransform(DPR,0,0,DPR,0,0);
}
addEventListener("resize",size); size();
function spawn(p){
  p.x=W/2; p.y=H-6;
  p.vx=(Math.random()-.5)*2.2;
  p.vy=-8-Math.random()*6;
  p.r=2+Math.random()*3;
  p.life=1;
  p.h=185+Math.random()*55;
}
for(let i=0;i<200;i++){let p={};spawn(p);p.y-=Math.random()*H*.8;ps.push(p)}
let last=performance.now();
function loop(t){
  let dt=Math.min((t-last)/16.67,2); last=t;
  x.fillStyle="rgba(16,21,31,.28)"; x.fillRect(0,0,W,H);
  let wx=parseFloat(wind.value)*dt, g=.18*dt;
  for(const p of ps){
    p.vx+=wx; p.vy+=g; p.x+=p.vx*dt; p.y+=p.vy*dt;
    if(p.x<p.r){p.x=p.r;p.vx*=-.55}
    if(p.x>W-p.r){p.x=W-p.r;p.vx*=-.55}
    if(p.y>H-p.r){
      p.y=H-p.r; p.vy*=-.48; p.vx*=.72;
      if(Math.abs(p.vy)+Math.abs(p.vx)<1.1)spawn(p);
    }
    p.life*=.997;
    if(p.y>H+20||p.life<.08)spawn(p);
    x.beginPath();
    x.fillStyle=`hsla(${p.h},90%,62%,${Math.max(.25,p.life)})`;
    x.arc(p.x,p.y,p.r,0,Math.PI*2);
    x.fill();
  }
  requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
</script>
</body>
</html>
Claude HaikuOK★ 8.5/10
Show code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Particle Fountain</title>
<style>
body { margin: 0; overflow: hidden; background: #1a1a1a; font-family: monospace; }
canvas { display: block; }
#wind { position: absolute; top: 10px; left: 10px; color: #aaa; }
</style>
</head>
<body>
<canvas id="c"></canvas>
<div id="wind">Wind: <input type="range" id="windSlider" min="-5" max="5" value="0" step="0.1"> <span id="windVal">0</span></div>
<script>
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
canvas.width = 1000;
canvas.height = 600;

let particles = [];
const g = 0.2;
let wind = 0;

const windSlider = document.getElementById('windSlider');
const windVal = document.getElementById('windVal');
windSlider.addEventListener('input', (e) => {
  wind = parseFloat(e.target.value);
  windVal.textContent = wind.toFixed(1);
});

function emit() {
  for (let i = 0; i < 15; i++) {
    particles.push({
      x: canvas.width / 2 + (Math.random() - 0.5) * 20,
      y: canvas.height - 10,
      vx: (Math.random() - 0.5) * 4,
      vy: -Math.random() * 8 - 2,
      life: 1.0,
      r: Math.random() * 4 + 2
    });
  }
}

function update() {
  particles = particles.filter(p => {
    p.vy += g;
    p.vx += wind * 0.02;
    p.x += p.vx;
    p.y += p.vy;
    p.life -= 0.008;
    
    if (p.y >= canvas.height - 10) {
      p.vy *= -0.6;
      p.y = canvas.height - 10;
      p.life -= 0.02;
    }
    return p.life > 0;
  });
  
  if (Math.random() < 0.8) emit();
}

function draw() {
  ctx.fillStyle = '#1a1a1a';
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  
  ctx.strokeStyle = '#444';
  ctx.lineWidth = 2;
  ctx.beginPath();
  ctx.moveTo(0, canvas.height - 10);
  ctx.lineTo(canvas.width, canvas.height - 10);
  ctx.stroke();
  
  particles.forEach(p => {
    ctx.fillStyle = `rgba(100, 200, 255, ${p.life})`;
    ctx.beginPath();
    ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
    ctx.fill();
  });
}

function loop() {
  update();
  draw();
  requestAnimationFrame(loop);
}

emit();
loop();
</script>
</body>
</html>
Claude SonnetOK★ 8.5/10
Show code
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Particle Fountain</title>
<style>
body{margin:0;background:#0a1020;overflow:hidden;font-family:sans-serif}
#ui{position:fixed;top:10px;left:10px;color:#fff;z-index:2}
canvas{display:block}
</style></head>
<body>
<div id="ui">Wind: <span id="wv">0</span><br><input id="wind" type="range" min="-200" max="200" value="0"></div>
<canvas id="c"></canvas>
<script>
const cvs=document.getElementById('c'),ctx=cvs.getContext('2d');
function resize(){cvs.width=innerWidth;cvs.height=innerHeight}
resize();addEventListener('resize',resize);
const windEl=document.getElementById('wind'),wv=document.getElementById('wv');
let wind=0;
windEl.addEventListener('input',()=>{wind=+windEl.value;wv.textContent=wind;});
const g=500,floorY=()=>cvs.height-20;
const N=200,particles=[];
function spawn(p){
  p.x=cvs.width/2;p.y=cvs.height-20;
  const spread=0.5;
  const ang=-Math.PI/2+(Math.random()-0.5)*spread;
  const speed=250+Math.random()*150;
  p.vx=Math.cos(ang)*speed;p.vy=Math.sin(ang)*speed;
  p.energy=1;p.hue=180+Math.random()*80;
}
for(let i=0;i<N;i++){const p={};spawn(p);p.y-=Math.random()*400;particles.push(p);}
function frame(){
  ctx.fillStyle='rgba(10,16,32,0.2)';
  ctx.fillRect(0,0,cvs.width,cvs.height);
  ctx.strokeStyle='#345';ctx.beginPath();
  ctx.moveTo(0,floorY());ctx.lineTo(cvs.width,floorY());ctx.stroke();
  const dt=0.016;
  particles.forEach(p=>{
    p.vy+=g*dt;p.vx+=wind*dt;
    p.x+=p.vx*dt;p.y+=p.vy*dt;
    if(p.y>floorY()){
      p.y=floorY();
      p.vy*=-0.55;p.vx*=0.85;
      p.energy*=0.7;
    }
    if(Math.abs(p.vy)<15 && p.energy<0.15){spawn(p);}
    if(p.x<0||p.x>cvs.width){spawn(p);}
    ctx.beginPath();ctx.arc(p.x,p.y,2.5,0,7);
    ctx.fillStyle=`hsla(${p.hue},90%,60%,0.85)`;
    ctx.fill();
  });
  requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
</script></body></html>
Claude OpusOK★ 8.5/10
Show code
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Particle Fountain</title>
<style>html,body{margin:0;background:#050810;overflow:hidden;font-family:sans-serif}
canvas{display:block}#ui{position:fixed;top:10px;left:10px;color:#9cf}</style>
</head>
<body>
<div id="ui">Wind <input id="wind" type="range" min="-200" max="200" value="0"></div>
<canvas id="c"></canvas>
<script>
const cv=document.getElementById('c'),ctx=cv.getContext('2d'),wind=document.getElementById('wind');
let W,H;
function resize(){W=cv.width=innerWidth;H=cv.height=innerHeight;}
addEventListener('resize',resize);resize();
const g=300,N=200,P=[];
function spawn(p){p.x=W/2;p.y=H-10;p.vx=(Math.random()-0.5)*80;p.vy=-(300+Math.random()*250);p.life=1;p.h=180+Math.random()*80;}
for(let i=0;i<N;i++){P.push({});spawn(P[i]);}
let last=performance.now();
function step(t){const dt=Math.min((t-last)/1000,0.033);last=t;const w=+wind.value;
 ctx.fillStyle='rgba(5,8,16,0.35)';ctx.fillRect(0,0,W,H);
 for(const p of P){
  p.vy+=g*dt;p.vx+=w*dt;p.x+=p.vx*dt;p.y+=p.vy*dt;
  if(p.y>H-6){p.y=H-6;p.vy*=-0.55;p.vx*=0.8;p.life-=0.25;if(Math.abs(p.vy)<40&&p.life<0.5)spawn(p);}
  if(p.x<0||p.x>W)spawn(p);
  ctx.fillStyle='hsl('+p.h+',90%,60%)';ctx.beginPath();ctx.arc(p.x,p.y,2.5,0,7);ctx.fill();
 }
 requestAnimationFrame(step);}
requestAnimationFrame(step);
</script>
</body>
</html>
Claude FableOK★ 8.5/10
Claude Fable – rendered output R28
Show code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Particle Fountain</title>
<style>html,body{margin:0;height:100%;background:#070b14;overflow:hidden}canvas{display:block}
#ui{position:fixed;top:12px;left:12px;color:#cfd8dc;font:13px monospace;background:rgba(0,0,0,.45);padding:8px 12px;border-radius:8px;z-index:2}</style>
</head>
<body>
<div id="ui">Wind <input id="wind" type="range" min="-250" max="250" value="0" step="1"> <span id="wv">0</span></div>
<canvas id="c"></canvas>
<script>
const cv=document.getElementById('c'),ctx=cv.getContext('2d');
const slider=document.getElementById('wind'),wv=document.getElementById('wv');
let W,H;function resize(){W=cv.width=innerWidth;H=cv.height=innerHeight;}
resize();addEventListener('resize',resize);
const N=200,G=600,P=[];
function spawn(p,delay){p.x=W/2+(Math.random()-.5)*14;p.y=H-8;
p.vx=(Math.random()-.5)*90;p.vy=-(420+Math.random()*180);
p.hue=185+Math.random()*70;p.t=-(delay||0);}
for(let i=0;i<N;i++){const p={};spawn(p,i*0.012);P.push(p);}
let last=performance.now();
function frame(t){requestAnimationFrame(frame);
const dt=Math.min((t-last)/1000,0.033);last=t;
const wind=+slider.value;wv.textContent=wind;
ctx.fillStyle='rgba(7,11,20,0.28)';ctx.fillRect(0,0,W,H);
ctx.fillStyle='#22304a';ctx.fillRect(0,H-6,W,6);
for(const p of P){p.t+=dt;if(p.t<0)continue;
p.vy+=G*dt;p.vx+=wind*dt;p.x+=p.vx*dt;p.y+=p.vy*dt;
if(p.y>H-8){p.y=H-8;p.vy*=-0.55;p.vx*=0.75;
if(Math.abs(p.vy)<45){spawn(p,Math.random()*0.15);continue;}}
if(p.x<-30||p.x>W+30){spawn(p,Math.random()*0.15);continue;}
ctx.fillStyle='hsl('+p.hue+' 90% 65%)';
ctx.beginPath();ctx.arc(p.x,p.y,2.5,0,7);ctx.fill();}}
requestAnimationFrame(frame);
</script>
</body>
</html>