// 006 - Curl Vector Field (fluid)
// 1:1 Original algorithm engine source
function createCurlVectorField() {
const MAX_PARTICLES = 1800;
const px = new Float32Array(MAX_PARTICLES);
const py = new Float32Array(MAX_PARTICLES);
const eps = 0.01;
return {
setup(context) {
for (let i = 0; i < MAX_PARTICLES; i++) {
px[i] = Math.random() * context.width;
py[i] = Math.random() * context.height;
}
},
render(context, timeState, params) {
const { ctx, width, height } = context;
const count = Math.min(MAX_PARTICLES, Number(params.particleCount || 1200));
const scale = Number(params.fieldScale || 35e-4);
const speed = Number(params.speed || 2.2);
const t = timeState.time * 0.12;
ctx.fillStyle = "rgba(8, 9, 13, 0.12)";
ctx.fillRect(0, 0, width, height);
for (let i = 0; i < count; i++) {
const nx = px[i] * scale;
const ny = py[i] * scale;
const n1 = noise2D(nx, ny + eps + t);
const n2 = noise2D(nx, ny - eps + t);
const vx = (n1 - n2) / (2 * eps);
const n3 = noise2D(nx + eps, ny + t);
const n4 = noise2D(nx - eps, ny + t);
const vy = -(n3 - n4) / (2 * eps);
const prevX = px[i];
const prevY = py[i];
px[i] += vx * speed * 8;
py[i] += vy * speed * 8;
if (px[i] < 0) px[i] += width;
if (px[i] > width) px[i] -= width;
if (py[i] < 0) py[i] += height;
if (py[i] > height) py[i] -= height;
const velMag = Math.sqrt(vx * vx + vy * vy);
const hue = (210 + velMag * 120 + t * 40) % 360;
ctx.strokeStyle = hsla(hue, 85, 62, 0.6);
ctx.lineWidth = 1.3;
ctx.beginPath();
ctx.moveTo(prevX, prevY);
ctx.lineTo(px[i], py[i]);
ctx.stroke();
}
}
};
}
// Default parameters from content metadata
const defaultParams = [
{
"key": "particleCount",
"label": "Streamline Particles",
"type": "range",
"min": 400,
"max": 1800,
"step": 100,
"defaultValue": 1200,
"description": "Particle density"
},
{
"key": "speed",
"label": "Flow Velocity",
"type": "range",
"min": 0.5,
"max": 5,
"step": 0.1,
"defaultValue": 2.2,
"description": "Particle advection speed"
},
{
"key": "fieldScale",
"label": "Field Scale",
"type": "range",
"min": 0.001,
"max": 0.008,
"step": 0.0005,
"defaultValue": 0.0035,
"description": "Vorticity wave scale"
}
];
if (!window.__art_instances) window.__art_instances = {};
if (!window.__art_instances['curl-vector-field']) {
const inst = typeof createCurlVectorField === 'function' ? createCurlVectorField() : null;
if (inst && inst.setup) {
inst.setup({ ctx, width, height, dpr: 1, aspectRatio: width / height }, defaultParams);
}
window.__art_instances['curl-vector-field'] = inst;
}
const instance = window.__art_instances['curl-vector-field'];
if (instance && instance.render) {
instance.render(
{ ctx, width, height, dpr: 1, aspectRatio: width / height },
{ time, deltaTime: dt, frameCount: Math.floor(time * 60), fps: 60 },
defaultParams
);
}