The technique you are looking for to achieve the gradually changing sizes is the use of a two dimensional noise function such as Perlin Noise. P5.js provides this as a utility function: noise()
. I've previously written a tutorial on using the noise()
function over on OpenProcessing.
In this case it looks like you will want to draw the circles in multiple passes with different noise seeds. During each pass you draw yellow or blue circles with varying diameters based on the noise value for the current x, y coordinate (it is a little hard to tell if green circles are also being drawn, or if they green is just the result of mixing colors).
Unfortunately color mixing with pigments is different than color mixing with the RGB model. In RGB, mixing Yellow and Blue doesn't get you Green, it gets you Grey/White. You can enable simple mixing by using blendMode(ADD)
, but the results aren't great with the original color scheme. One of the interesting color mixes in the RGB model is Red + Green which does get you yellow. So that is what I've gone with in my example. Note: when using blendMode(ADD)
it is important not to draw a light background! If you do that can throw off the color blending. Instead leave the canvas background blank and make the HTML element that contains it have your desired background.
let spaceX = 6,
spaceY = 6;
function setup() {
createCanvas(400, 400);
noFill();
strokeWeight(2);
ellipseMode(CENTER);
// comment this out for animation
noLoop();
}
function draw() {
clear();
// Set the seed for the random noise
noiseSeed(37);
// Color: red
stroke(255, 0, 0);
// Draw circles with max stroke 2 and max diam 8
pass(3, 8, 0);
// Change the seed for the random noise
noiseSeed(73);
// Set the blendMode to add so that when the red and green circles overlapp their color is combined (instead of the second circle replacing the first).
blendMode(ADD);
stroke(0, 255, 0);
pass(2, 10, 1);
}
// note: I've parameterized the stroke weight, diameter, and a position offset
// This makes it possible to tweak the different color circles to get different effects
function pass(weight, diam, offset) {
// horizontal row
for (let x = 0; x <= width; x += spaceX) {
// vertical column
for (let y = 0; y <= height; y += spaceY) {
// Note: the use of millis() as a third parameter makes this animated if you re-run this over time
let n = noise(x / 200, y / 200, millis() / 10000);
strokeWeight(n * weight);
ellipse(x + offset, y + offset, n * diam);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.js"></script>
Another option for achieving the variation between the circles of the two different colors would be to use a slight offset in the Z dimension input to the noise function.
let spaceX = 6,
spaceY = 6;
function setup() {
createCanvas(400, 400);
noFill();
strokeWeight(2);
ellipseMode(CENTER);
// comment this out for animation
noLoop();
}
function draw() {
clear();
// Set the seed for the random noise
noiseSeed(37);
// Color: red
stroke(255, 0, 0);
// Draw circles with max stroke 2 and max diam 8
pass(3, 8, 0, 0);
// Change the seed for the random noise
// noiseSeed(73);
// Set the blendMode to add so that when the red and green circles overlapp their color is combined (instead of the second circle replacing the first).
blendMode(ADD);
stroke(0, 255, 0);
pass(2, 10, 1, 0.3);
}
// note: I've parameterized the stroke weight, diameter, and a position offset
// This makes it possible to tweak the different color circles to get different effects
function pass(weight, diam, offset, zoff) {
// horizontal row
for (let x = 0; x <= width; x += spaceX) {
// vertical column
for (let y = 0; y <= height; y += spaceY) {
// Note: the use of millis() as a third parameter makes this animated if you re-run this over time
let n = noise(x / 200, y / 200, zoff + millis() / 10000);
strokeWeight(n * weight);
ellipse(x + offset, y + offset, n * diam);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.js"></script>