20260310
p5.js
Generative Art particle
View Source Code
let video;
let myGeom;
let pointShader;
const vertSource = `
precision highp float;
attribute vec3 aPosition;
attribute vec2 aTexCoord;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
uniform sampler2D videoBuffer;
uniform float time;
uniform vec2 uMouse;
varying vec4 vColor;
// Pseudo-random noise
float rand(vec2 n) {
return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453);
}
// 2D Noise
float noise(vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
// Four corners
float a = rand(i);
float b = rand(i + vec2(1.0, 0.0));
float c = rand(i + vec2(0.0, 1.0));
float d = rand(i + vec2(1.0, 1.0));
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(a, b, u.x) +
(c - a) * u.y * (1.0 - u.x) +
(d - b) * u.x * u.y;
}
void main() {
vec2 uv = vec2(1.0 - aTexCoord.x, aTexCoord.y); // Mirror the X axis for webcam
vec4 texColor = texture2D(videoBuffer, uv);
// Ignore transparent or fully black if we wanted to cull,
// but in shader we usually just draw it anyway.
float brightness = dot(texColor.rgb, vec3(0.299, 0.587, 0.114));
vec3 pos = aPosition;
// 1. Z-depth displacement based on brightness
// Brighter pixels pop out towards the camera
pos.z += brightness * 200.0;
// 2. Organic movement (noise based on time and UV)
float nVal = noise(uv * 10.0 + time * 0.5);
pos.x += (nVal - 0.5) * 15.0;
pos.y += (noise(uv * 10.0 + time * 0.5 + 100.0) - 0.5) * 15.0;
pos.z += (noise(uv * 10.0 + time * 0.5 + 200.0) - 0.5) * 15.0;
// 3. Optional: Mouse repulsion
// Calculate distance from vertex XY to Mouse XY
// uMouse is normalized 0-1 across the canvas
vec2 centerPos = aTexCoord;
float d = distance(centerPos, uMouse);
if (d < 0.2) {
float force = (0.2 - d) / 0.2; // 0 to 1
pos.z += force * 300.0; // Pushed towards the viewer
pos.x += (rand(uv) - 0.5) * force * 100.0;
pos.y += (rand(uv + 1.0) - 0.5) * force * 100.0;
}
// Set position
gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(pos, 1.0);
vColor = vec4(texColor.rgb, 1.0);
}
`;
const fragSource = `
precision highp float;
varying vec4 vColor;
void main() {
// Simple circular alpha masking could be added here if needed,
// but since we are drawing quads, they look fine as small squares or triangles.
gl_FragColor = vColor;
}
`;
function setup() {
createCanvas(windowWidth, windowHeight, WEBGL);
// p5.js 2.x feature: ensure pixelDensity is 1 for consistent shader mapping
pixelDensity(1);
// Capture webcam video
video = createCapture(VIDEO);
video.size(320, 240); // Internal resolution
video.hide();
// Create our custom fast 3D shader
pointShader = createShader(vertSource, fragSource);
// Normalizing texture coords so u,v is 0.0 to 1.0
textureMode(NORMAL);
// Build a static dense geometry representing particles
myGeom = buildGeometry(() => {
// Grid resolution (160x120 = 19,200 quads = fast on modern GPUs)
let resX = 160;
let resY = 120;
// Scale to fit screen
let w = width * 0.8;
let h = w * (resY / resX);
if (h > height * 0.8) {
h = height * 0.8;
w = h * (resX / resY);
}
noStroke();
let quadSize = (w / resX) * 0.8;
for (let y = 0; y < resY; y++) {
for (let x = 0; x < resX; x++) {
// Physical position of the quad center
let px = map(x, 0, resX - 1, -w / 2, w / 2);
let py = map(y, 0, resY - 1, -h / 2, h / 2);
// UV coordinate maps to the video texture
let u = x / (resX - 1);
let v = y / (resY - 1);
// Draw one small quad (2 triangles)
beginShape(TRIANGLES);
let s = quadSize * 0.5;
// Top-left triangle
vertex(px - s, py - s, 0, u, v);
vertex(px + s, py - s, 0, u, v);
vertex(px - s, py + s, 0, u, v);
// Bottom-right triangle
vertex(px + s, py - s, 0, u, v);
vertex(px + s, py + s, 0, u, v);
vertex(px - s, py + s, 0, u, v);
endShape();
}
}
});
}
function draw() {
background(20);
// Allows the user to rotate the canvas 3D space with the mouse
orbitControl();
shader(pointShader);
// Pass variables to the shader
pointShader.setUniform("videoBuffer", video);
pointShader.setUniform("time", millis() / 1000.0);
// Pass normalized mouse coordinates
let mx = constrain(mouseX / width, 0, 1);
let my = constrain(mouseY / height, 0, 1);
pointShader.setUniform("uMouse", [mx, my]);
// Render the geometry we built
model(myGeom);
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
}