Voronoi Cells
About This Sketch
Voronoi diagrams appear throughout natureβin giraffe spots, cracked mud, and cell structures. Each cell represents the region closer to one seed point than any other, creating natural-looking organic boundaries.
As the seed points drift slowly across the canvas, the cells reshape and flow like living tissue. This mathematical structure has applications from biology to computer graphics to urban planning.
Algorithm
Voronoi diagrams partition space based on distance to seed points. Each pixel is colored by its nearest point, creating organic cell-like regions.
**Key Concepts:**
- **Voronoi Tessellation**: Partitioning space by nearest neighbor
- **Distance Field**: Each point defines a region of influence
- **Cellular Patterns**: Natural looking cell divisions
- **Nearest Neighbor Search**: Finding closest point for each pixel
- **Dynamic Points**: Seeds move, causing cells to morph
**How it works:**
1. Place seed points randomly on canvas
2. For each pixel, find the nearest seed point
3. Color pixel based on which point is closest
4. This creates boundaries equidistant between neighboring points
5. Move seed points slowly (random walk)
6. Cells dynamically reshape as seeds move
7. Draw seed points on top for visualization
Pseudocode
SETUP:
FOR i = 0 to numPoints:
CREATE point with:
position = random
velocity = random(-0.5, 0.5)
DRAW (every frame):
CLEAR background
FOR EACH point:
position = position + velocity
IF hitting boundary:
velocity = -velocity
FOR x = 0 to canvas_width STEP 2:
FOR y = 0 to canvas_height STEP 2:
min_distance = infinity
closest_point = null
FOR EACH point:
distance = euclidean_distance(x, y, point)
IF distance < min_distance:
min_distance = distance
closest_point = point
color = color_for_point(closest_point)
DRAW pixel at (x, y)
FOR EACH point:
DRAW circle at point position
Source Code
let sketch = function(p) {
let points = [];
let numPoints = 15;
p.setup = function() {
p.createCanvas(400, 300);
p.colorMode(p.RGB);
for (let i = 0; i < numPoints; i++) {
points.push({
x: p.random(p.width),
y: p.random(p.height),
vx: p.random(-0.15, 0.15),
vy: p.random(-0.15, 0.15)
});
}
};
p.draw = function() {
const colors = getThemeColors();
p.background(...colors.bg);
// Update points
for (let pt of points) {
pt.x += pt.vx;
pt.y += pt.vy;
if (pt.x < 0 || pt.x > p.width) pt.vx *= -1;
if (pt.y < 0 || pt.y > p.height) pt.vy *= -1;
}
// Draw Voronoi cells
p.loadPixels();
for (let x = 0; x < p.width; x += 2) {
for (let y = 0; y < p.height; y += 2) {
let minDist = Infinity;
let closest = 0;
for (let i = 0; i < points.length; i++) {
let d = p.dist(x, y, points[i].x, points[i].y);
if (d < minDist) {
minDist = d;
closest = i;
}
}
let t = closest / points.length;
let col = t < 0.33 ? colors.accent1 :
t < 0.66 ? colors.accent2 : colors.accent3;
p.stroke(...col, 150);
p.strokeWeight(1);
p.point(x, y);
}
}
// Draw center points
for (let i = 0; i < points.length; i++) {
p.fill(...colors.accent2);
p.noStroke();
p.circle(points[i].x, points[i].y, 6);
}
};
};