
I've included code below for Processing (Java mode) that generates a variety of random symmetrical patterns using different algorithms.
Video example:
Just copy and paste it into your Processing window. Make sure you're using Java mode!
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
int frameIndex = 0;
String outputFolder;
String[] patternModes = {
"dither", "grayscale", "bigcircle", "bigarc", "ink_blot",
"radialspread", "organicmesh", "fragmented_noise", "glitch"
};
// For each algorithm we store a random scale (so it doesn't change within a series)
HashMap<String, Float> modeScale = new HashMap<String, Float>();
public void settings() {
size(1024, 768);
}
public void setup() {
frameRate(5);
noStroke();
// Create a unique folder with current date and time
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");
outputFolder = "d://patterns/pattern_" + sdf.format(new Date());
File folder = new File(outputFolder);
if (!folder.exists()) {
folder.mkdirs();
}
}
public void draw() {
background(255);
int totalFramesPerMode = 5;
int currentModeIndex = frameIndex / totalFramesPerMode;
if (currentModeIndex >= patternModes.length) {
noLoop();
return;
}
String mode = patternModes[currentModeIndex];
println("Frame " + frameIndex + " mode: " + mode);
// If the scale for this algorithm hasn't been chosen yet - set it
if (!modeScale.containsKey(mode)) {
modeScale.put(mode, random(0.5f, 2.0f));
}
float elementScale = modeScale.get(mode);
println("Scale factor: " + elementScale);
// loop_t changes from 0 to 1 over 30 frames and then repeats
float loop_t = (frameIndex % totalFramesPerMode) / (float)totalFramesPerMode;
int margin = 30; // Border width
int cellSize = 10; // Inner grid cell size
int effectiveWidth = width - 2 * margin;
int effectiveHeight = height - 2 * margin;
// Draw the pattern in the left half of the inner area
drawInnerPattern(mode, margin, cellSize, effectiveWidth, effectiveHeight, elementScale, loop_t);
// Mirror the inner area vertically for complete symmetry
mirrorInnerVertically(margin, effectiveWidth, effectiveHeight);
// Draw a black border around the edges
drawBorder(margin);
// Save the frame as an image
String filename = outputFolder + "/frame-" + nf(frameIndex, 4) + ".png";
saveFrame(filename);
frameIndex++;
if (frameIndex >= patternModes.length * totalFramesPerMode) {
noLoop();
}
}
// Function for cyclical noise.
// We use a technique where coordinates are shifted along a circle, which ensures a cycle.
float loopNoise(float a, float b, float t) {
float r = 100; // Radius - adjust for desired effect
float nx = cos(TWO_PI t) r;
float ny = sin(TWO_PI t) r;
return noise(a + nx, b + ny);
}
public void drawInnerPattern(String mode, int margin, int cellSize, int effectiveWidth, int effectiveHeight, float elementScale, float loop_t) {
for (int y = margin; y < margin + effectiveHeight; y += cellSize) {
for (int x = margin; x < margin + effectiveWidth/2; x += cellSize) {
float n = loopNoise((x - margin) 0.05f, (y - margin) 0.05f, loop_t);
drawCell(x, y, cellSize, n, mode, elementScale, loop_t);
int x_offset = x - margin;
int mirrorx = margin + effectiveWidth - xoffset - cellSize;
drawCell(mirrorx, y, cellSize, n, mode, elementScale, loopt);
}
}
}
public void drawCell(int x, int y, int cellSize, float n, String mode, float elementScale, float loop_t) {
if (mode.equals("dither")) {
noStroke();
fill(n < 0.5 ? 0 : 255);
rect(x, y, cellSize, cellSize);
} else if (mode.equals("grayscale")) {
noStroke();
int shade = (int)(n * 255);
fill(shade);
rect(x, y, cellSize, cellSize);
} else if (mode.equals("big_circle")) {
noStroke();
fill(0);
pushMatrix();
translate(x + cellSize/2, y + cellSize/2);
float d = cellSize (0.8f + 0.4f n) * elementScale;
ellipse(0, 0, d, d);
popMatrix();
} else if (mode.equals("big_arc")) {
noFill();
stroke(0);
strokeWeight(1.5f);
pushMatrix();
translate(x + cellSize/2, y + cellSize/2);
float startangle = n * TWOPI;
float arcextent = PI + n * HALFPI;
float d = cellSize (1.2f + 0.5f n) * elementScale;
arc(0, 0, d, d, startangle, startangle + arc_extent);
popMatrix();
} else if (mode.equals("ink_blot")) {
noStroke();
fill(0);
pushMatrix();
translate(x + cellSize/2, y + cellSize/2);
beginShape();
int num_vertices = (int)random(8, 16);
for (int i = 0; i < num_vertices; i++) {
float angle = map(i, 0, numvertices, 0, TWOPI);
float base_r = (cellSize/2.0f) * elementScale;
// Calculate the third parameter separately to call noise with three arguments
float thirdParam = cos(TWOPI * loopt) * 0.5f + sin(TWOPI * loopt) * 0.5f;
float r = base_r (0.5f + 0.5f noise(cos(angle) + n, sin(angle) + n, thirdParam));
r *= random(0.8f, 1.2f);
vertex(r cos(angle), r sin(angle));
}
endShape(CLOSE);
popMatrix();
} else if (mode.equals("radial_spread")) {
stroke(0);
strokeWeight(1);
pushMatrix();
translate(x + cellSize/2, y + cellSize/2);
int num_lines = (int)(4 + n * 8);
for (int i = 0; i < num_lines; i++) {
float angle = map(i, 0, numlines, 0, TWOPI);
float length = cellSize (0.5f + n elementScale);
if (loopNoise(cos(angle) + 0.1f, sin(angle) + 0.1f, loop_t) > 0.4f) {
line(0, 0, length cos(angle), length sin(angle));
}
}
popMatrix();
} else if (mode.equals("organic_mesh")) {
noFill();
stroke(0);
strokeWeight(1);
pushMatrix();
translate(x, y);
int num_points = 4 + (int)(n * 4);
beginShape();
for (int i = 0; i < num_points; i++) {
float px = random(0, cellSize);
float py = random(0, cellSize);
curveVertex(px, py);
}
endShape();
popMatrix();
} else if (mode.equals("fragmented_noise")) {
noStroke();
int num_fragments = 4;
float fragSize = cellSize / 2.0f;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
float fragnoise = loopNoise((x + i fragSize) 0.1f, (y + j fragSize) 0.1f, loopt);
if (frag_noise > 0.4f) {
fill(0);
} else {
fill(255);
}
rect(x + i fragSize, y + j fragSize, fragSize, fragSize);
}
}
} else if (mode.equals("glitch")) {
noStroke();
fill(0);
pushMatrix();
float dx = random(-cellSize 0.2f, cellSize 0.2f);
float dy = random(-cellSize 0.2f, cellSize 0.2f);
translate(x + cellSize/2 + dx, y + cellSize/2 + dy);
for (int i = 0; i < 3; i++) {
int alpha_val = (int)(150 - i * 40);
fill(0, alpha_val);
float sizeoffset = cellSize (0.5f + 0.5f loopNoise(n, i, loopt));
rectMode(CENTER);
rect(0, 0, sizeoffset * elementScale, sizeoffset * elementScale);
}
popMatrix();
}
}
public void mirrorInnerVertically(int margin, int effectiveWidth, int effectiveHeight) {
loadPixels();
int inner_top = margin;
int inner_bottom = margin + effectiveHeight;
int halfinnerheight = effectiveHeight / 2;
for (int y = innertop; y < innertop + halfinnerheight; y++) {
int mirrory = innerbottom - 1 - (y - inner_top);
for (int x = margin; x < margin + effectiveWidth; x++) {
int topIndex = x + y * width;
int bottomIndex = x + mirror_y * width;
pixels[bottomIndex] = pixels[topIndex];
}
}
updatePixels();
}
public void drawBorder(int margin) {
noStroke();
fill(0);
rect(0, 0, width, margin);
rect(0, height - margin, width, margin);
rect(0, margin, margin, height - 2 * margin);
rect(width - margin, margin, margin, height - 2 * margin);
}
A Little Note
I'm sharing this code freely because I love creating and sharing interesting visual patterns with the community. While I certainly don't expect anything in return, if you find this useful or it sparks joy in your projects, I wouldn't mind a coffee! ☕
Your support helps me continue creating and sharing more free resources like this one. Either way, I hope you enjoy playing with these symmetrical patterns as much as I enjoyed making them!
