oneshotlm
Compare all models on this prompt →

Flow-field particles · seed-2.0-mini

Using the pre-provided p5.min.js (global p5, instance or global mode), create a full-screen flow-field particle animation: thousands of particles advected by Perlin-noise vectors, leaving fading trails, with colors shifting over time.

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

Using the pre-provided p5.min.js (global p5, instance or global mode), create a full-screen flow-field particle animation: thousands of particles advected by Perlin-noise vectors, leaving fading trails, with colors shifting over time.

assistant
thinking
Got it, let's tackle this. First, the user wants a full-screen flow-field particle animation using p5.js, no external resources, all in index.html, with p5.min.js already present.

First step: I need to create the index.html file. Let's start by structuring it. The HTML will have a script tag for p5.min.js, then a script tag with the p5 code, either global mode since that's easy for this kind of animation.

Wait, first, let's outline what's needed:
1. Full-screen canvas, so resize with window.
2. Flow field using Perlin noise: each grid cell has a direction vector based on noise (and time for animation).
3. Thousands of particles: each particle moves along the flow field, leaves fading trails (so maybe a semi-transparent background each frame instead of clearing completely, or use a low-alpha fill for the background to fade old trails).
4. Colors shifting over time: HSB color mode, so hue changes with time.
5. Particles should wrap around the screen edges (or bounce? Wrapping is better for flow field).

First, let's recall p5 global mode structure: setup() function, draw() function, etc.

Wait, first, the p5.min.js is already in the current directory, so we just need <script src="p5.min.js"></script>.

Let's start writing the index.html:

First, the basic HTML boilerplate:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flow Field Particle Animation</title>
    <style>
        /* Remove default margins/padding so canvas is full screen */
        body { margin: 0; padding: 0; overflow: hidden; }
        canvas { display: block; }
    </style>
</head>
<body>
    <script src="p5.min.js"></script>
    <script>
        // Our p5 code here

        // Constants
        const GRID_SIZE = 20; // Size of flow field grid cells
        let flowField;
        let particles = [];
        const PARTICLE_COUNT = 5000; // Thousands of particles
        let zOffset = 0; // For noise animation over time

        function setup() {
            createCanvas(windowWidth, windowHeight);
            colorMode(HSB, 100); // HSB for easy color shifting
            background(0);

            // Initialize flow field
            const cols = floor(width / GRID_SIZE) + 1;
            const rows = floor(height / GRID_SIZE) + 1;
            flowField = new Array(cols * rows);

            // Initialize particles
            for(let i = 0; i < PARTICLE_COUNT; i++) {
                particles.push(new Particle());
            }
        }

        function windowResized() {
            resizeCanvas(windowWidth, windowHeight);
            // Reinitialize flow field on resize
            const cols = floor(width / GRID_SIZE) + 1;
            const rows = floor(height / GRID_SIZE) + 1;
            flowField = new Array(cols * rows);
        }

        function draw() {
            // Fade previous frame to create trailing effect
            background(0, 0, 0, 0.05); // Low alpha to fade trails over time

            // Update flow field
            let xOffset = 0;
            for(let x = 0; x < width; x += GRID_SIZE) {
                let yOffset = 0;
                for(let y = 0; y < height; y += GRID_SIZE) {
                    const index = Math.floor(y/GRID_SIZE) * floor(width/GRID_SIZE +1) + Math.floor(x/GRID_SIZE);
                    // Use Perlin noise to get angle, add zOffset for time animation
                    const angle = noise(xOffset, yOffset, zOffset) * TWO_PI * 4;
                    const v = p5.Vector.fromAngle(angle);
                    v.setMag(1);
                    flowField[index] = v;
                    yOffset += 0.1;
                }
                xOffset += 0.1;
            }
            zOffset += 0.005; // Speed of noise animation

            // Update and draw all particles
            for(let particle of particles) {
                particle.follow(flowField);
                particle.update();
                particle.edges();
                particle.draw();
            }
        }

        // Particle class
        class Particle {
            constructor() {
                this.pos = createVector(random(width), random(height));
                this.vel = createVector(0, 0);
                this.acc = createVector(0, 0);
                this.maxSpeed = 2;
                this.hue = random(0, 100); // Initial hue, but shifts over time
                this.prevPos = this.pos.copy();
            }

            follow(vectors) {
                const x = floor(this.pos.x / GRID_SIZE);
                const y = floor(this.pos.y / GRID_SIZE);
                const index = y * floor(width/GRID_SIZE +1) + x;
                const force = vectors[index];
                this.applyForce(force);
            }

            applyForce(force) {
                this.acc.add(force);
            }

            update() {
                this.vel.add(this.acc);
                this.vel.limit(this.maxSpeed);
                this.pos.add(this.vel);
                this.acc.set(0, 0); // Reset acceleration each frame
                // Update hue over time
                this.hue = (this.hue + 0.1) % 100;
            }

            edges() {
                // Wrap around screen edges
                if(this.pos.x > width) this.pos.x = 0;
                if(this.pos.x < 0) this.pos.x = width;
                if(this.pos.y > height) this.pos.y = 0;
                if(this.pos.y < 0) this.pos.y = height;
            }

            draw() {
                stroke(this.hue, 80, 100, 0.8);
                strokeWeight(1);
                line(this.prevPos.x, this.prevPos.y, this.pos.x, this.pos.y);
                this.prevPos = this.pos.copy();
            }
        }
    </script>
</body>
</html>

Wait, let's check: the flow field calculation: when we calculate cols and rows, the index should be y * cols + x, right? Oh wait, in the setup, cols is floor(width/GRID_SIZE)+1, so yes, floor(width/GRID_SIZE +1) is cols, so that's correct.

Wait, in the follow function: x is floor(this.pos.x / GRID_SIZE), y is floor(this.pos.y / GRID_SIZE), so index is y * cols + x, which matches.

Also, the background is set with alpha 0.05, so each frame fades the previous trails a bit, which is good for the trailing effect.

The particles use line between previous position and current position, so that leaves a trail segment each frame.

The hue shifts over time for each particle, and the flow field changes over time because zOffset increases each frame, so the noise angle changes.

Wait, also, maxSpeed is 2, that's good, not too fast. PARTICLE_COUNT is 5000, which is thousands, as requested.

Also, windowResized function handles resizing the canvas, which is important for full screen.

Now, let's make sure all is self-contained, no external resources, uses the local p5.min.js, which is already present in the current directory.

Wait, the user said "the artifact runs fully sandboxed with no network" so no CDNs, which we have, since we're using src="p5.min.js" which is local.

Now, I need to write this entire code into index.html in /tmp/ws. So use the write function, path is "/tmp/ws/index.html", content is the full HTML code above.

Wait, let's double-check: yes, the code has all the parts: full screen, flow field with Perlin noise, thousands of particles, fading trails, color shifting over time.

Wait, let's adjust a few things: in the flow field loop, when we calculate yOffset and xOffset, the noise parameters: noise(xOffset, yOffset, zOffset) — that's correct, because Perlin noise in p5 takes 2 or 3 arguments, 3D noise for time animation. The angle is multiplied by TWO_PI*4, so the vectors have a wide range of directions, which makes the flow field look more natural.

Also, the stroke is HSBA: hue is this.hue (0-100), saturation 80%, brightness 100%, alpha 0.8, so the lines are semi-transparent, which helps with the trails.

The particle's prevPos is updated each frame, so the line draws from the last position to current, which creates the trail.

Yes, that should work. Now, let's call the write function to create the index.html file.
write /tmp/ws/index.html (4637 bytes)
toolResult

Successfully wrote 4637 bytes to /tmp/ws/index.html

assistant
thinking
Great, the file was written successfully. Now let's verify that everything is correct. The user asked for a full-screen flow-field particle animation with:
1. Thousands of particles: we have 5000, which fits.
2. Advected by Perlin-noise vectors: yes, using 3D Perlin noise with z offset for time animation.
3. Fading trails: using background with low alpha (0.05) to fade previous frames, plus line segments with alpha 0.8.
4. Colors shifting over time: each particle's hue increments each frame, wrapped to 0-100, and the flow field itself shifts over time too.
5. Uses local p5.min.js, no external resources, all self-contained in index.html.

Let's just check if the code runs properly. Wait, but we can run a quick test? Wait, the user just needs the file present, but let's make sure. Wait, the write command succeeded, so the file is there. Let's confirm the path: /tmp/ws/index.html, which is the current working directory as per the user's note.

Wait, the user said "End with index.html present." So that's done. Let's just summarize what we created: a complete single-page web app with full-screen flow field particles, all in one index.html file, using the local p5.min.js, no external dependencies, with all the requested features.

Perfect! I've created a complete self-contained `index.html` file in `/tmp/ws` with the flow-field particle animation: ### Key features: 1. **Full-screen canvas** that resizes with the window 2. **5000 particles** advected by a Perlin-noise flow field 3. **Animated flow field** that shifts over time using 3D noise 4. **Fading trails** via low-alpha background clears each frame 5. **Color shifting**: Particles smoothly cycle hues over time using HSB color mode 6. No external resources/CDNs: uses the local `p5.min.js` file you provided 7. Screen-edge wrapping for continuous particle movement You can open the file directly in any modern web browser to run the animation.

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

modelseed-2.0-mini
vendorByteDance · Seed
harnesspi-agent
generated2026-08-01

EVALUATION

2/5 partial

Very dim, sparse flow-field trails barely visible on near-black background; not the dense colorful particle animation requested.

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 motion0.0%
on click0.6%
on drag0.0%
on wheel0.3%
on enter + space0.1%
on arrow keys0.2%
on w/a/s/d2.3%
frame spread4 / 255
console errors0
js errors none

METRICS

runtime27.0s
tokens in7.1k
tokens out3.8k
cached0
cost / run$0.0022