Solving the Overlap Problem in Annular Arrangements: Creative Coding Cooking in 3 Minutes
Here's a quick 3-minute read on creative coding. We'll be looking at how to solve the overlap problem in annular arrangements.
The overlap problem in an annular arrangement means the last object will be on the first object if you just drew objects simply.
考え中🤔 pic.twitter.com/mK5pUTUYNb
— センバク (@senbaku) October 25, 2025
Then, @senbaku's child solved this problem with the brilliant idea of using half-circles for the first and the last parts.
なるほどなー。
— センバク (@senbaku) October 25, 2025
こども案、円周上に並べた円の最初と最後に、半円を追加することで重なりをいい感じに見せる、というものだった。簡単でスマートやん...。#p5js pic.twitter.com/nWNpUWU12i
Building on this idea, let's explore concrete implementation methods in p5.js.
[BRUTE FORCE?] Redrawing Only the Unwanted Overlap
As a sly adult who has always gotten by with a bit of trickery, the idea I came up with is to simply "cover up the problematic part".
Specifically, we redraw only the area where the last object overlaps the first one.
/**
* p5.js code example
* Redraw only the area where the last object overlaps the first one.
*
* Solving the overlap problem in an annular arrangement
* Creative Coding Cooking in 3 Minutes
*
* @author @deconbatch
* @version 0.1
* @license CC0 1.0 https://creativecommons.org/publicdomain/zero/1.0/deed.ja
* p5.js 1.11.11
* created 2025.10.28
*/
function setup() {
const CANVAS_SIZE = 520;
const DONUT_SIZE_RATIO = 0.3;
const OBJ_NUM = 8;
const OBJ_SIZE = DONUT_SIZE_RATIO * CANVAS_SIZE;
createCanvas(CANVAS_SIZE, CANVAS_SIZE);
translate(width * 0.5, height * 0.5);
background("SteelBlue");
fill("LightSteelBlue");
stroke("Snow");
strokeWeight(10);
for (let t = 0; t < TWO_PI; t += TWO_PI / OBJ_NUM) {
circle(
OBJ_SIZE * cos(t),
OBJ_SIZE * sin(t),
OBJ_SIZE
);
}
arc(
OBJ_SIZE * cos(0),
OBJ_SIZE * sin(0),
OBJ_SIZE,
OBJ_SIZE,
PI,
TWO_PI,
OPEN
);
}
[CHANGE OF PERSPECTIVE] You Don't Need no Overlap
The root of the problem is the "overlap". Therefore, we should use an object that allows for an annular arrangement without any overlap.
Specifically, how about the object like this?
By connecting these objects sequentially, we can achieve an annular arrangement without any overlap.
/**
* p5.js code example
* Use an object that allows for an annular arrangement without any overlap.
*
* Solving the overlap problem in an annular arrangement
* Creative Coding Cooking in 3 Minutes
*
* @author @deconbatch
* @version 0.1
* @license CC0 1.0 https://creativecommons.org/publicdomain/zero/1.0/deed.ja
* p5.js 1.11.11
* created 2025.10.28
*/
function setup() {
const CANVAS_SIZE = 520;
const DONUT_SIZE_RATIO = 0.3;
const OBJ_NUM = 8;
const OBJ_SIZE = DONUT_SIZE_RATIO * CANVAS_SIZE;
const OBJ_THETA_STEP = TWO_PI / OBJ_NUM;
createCanvas(CANVAS_SIZE, CANVAS_SIZE);
translate(width * 0.5, height * 0.5);
background("SteelBlue");
fill("LightSteelBlue");
stroke("Snow");
strokeWeight(10);
for (let t = 0; t < TWO_PI; t += OBJ_THETA_STEP) {
push();
translate(OBJ_SIZE * cos(t), OBJ_SIZE * sin(t));
rotate(t);
arc(
0,
0,
OBJ_SIZE,
OBJ_SIZE,
0,
PI,
OPEN
);
arc(
OBJ_SIZE * (cos(OBJ_THETA_STEP) - 1),
OBJ_SIZE * sin(OBJ_THETA_STEP),
OBJ_SIZE,
OBJ_SIZE,
PI + OBJ_THETA_STEP,
TWO_PI + OBJ_THETA_STEP,
OPEN
);
pop();
}
}
The idea is elegant because it solve the problem with just a single loop. On the other hand, the complex math inside the arc() function might compromise its readability and versatility.
[FOR GREATER VERSATILITY] Faint Overlap via a Rendering Trick
The two methods described previously work well only for simple shapes like a perfect circle. However, applying them to the more complex shapes would be difficult.
Is there a more versatile approach that can handle objects like this?
By drawing the object onto a small createGraphics() buffer, it becomes possible to arrange them using the image() function, or to use upper/lower halves cut versions. Therefore, this approach serves as a more versatile version of the "Redrawing Only the Unwanted Overlap (Idea 1)".
Any complex shape drawn onto the createGraphics() buffer can be handled.
Another possible approach is to first draw the object onto the createGraphics() buffer, apply a transparent gradient to the lower half of the object, and then arrange the resulting buffers sequentially.
In short, the trick is to draw the portion that would be underneath faintly, using a semi-transparent color.
After 100 iterations of this drawing process, here is the result.
Not only does this approach maintain the elegance of a single loop, but it also allows for creative experimentation with various decorative effects, such as BLUR.
/**
* p5.js code example
* Drawing the portion that would be underneath faintly, using a semi-transparent color
*
* Solving the overlap problem in an annular arrangement
* Creative Coding Cooking in 3 Minutes
*
* @author @deconbatch
* @version 0.1
* @license CC0 1.0 https://creativecommons.org/publicdomain/zero/1.0/deed.ja
* p5.js 1.11.11
* created 2025.10.28
*/
function setup() {
const CANVAS_SIZE = 520;
const DONUT_SIZE_RATIO = 0.3;
const OBJ_NUM = 8;
const OBJ_SIZE = DONUT_SIZE_RATIO * CANVAS_SIZE;
const OBJ_THETA_STEP = TWO_PI / OBJ_NUM;
createCanvas(CANVAS_SIZE, CANVAS_SIZE);
imageMode(CENTER);
// Original object buffer
const obj = createGraphics(OBJ_SIZE, OBJ_SIZE);
obj.background(0, 0);
obj.fill("LightSteelBlue")
obj.stroke("Snow");
obj.strokeWeight(5);
obj.push();
obj.translate(OBJ_SIZE * 0.5, OBJ_SIZE * 0.5);
for (let i = 0.8; i > 0; i -= 0.2) {
let w = OBJ_SIZE * i;
obj.beginShape();
obj.vertex(random(-0.5, -0.4) * w, random(-0.5, -0.4) * w);
obj.vertex(random(0.4, 0.5) * w, random(-0.5, -0.4) * w);
obj.vertex(random(0.4, 0.5) * w, random(0.4, 0.5) * w);
obj.vertex(random(-0.5, -0.4) * w, random(0.4, 0.5) * w);
obj.endShape(CLOSE);
}
obj.pop();
// apply a transparent gradient buffer
const img = createGraphics(OBJ_SIZE, OBJ_SIZE);
img.image(obj, 0, 0);
img.strokeWeight(2);
img.blendMode(REMOVE);
for (let i = 0; i < OBJ_SIZE; i++) {
img.stroke(0, map(i, 0, OBJ_SIZE, 0, 256));
img.line(0, i, OBJ_SIZE, i);
}
// BLUR effect on original buffer
obj.filter(BLUR, 10);
translate(width * 0.5, height * 0.5);
background("SteelBlue");
// arrange the original buffer
for (let t = 0; t < TWO_PI; t += OBJ_THETA_STEP) {
push();
translate(OBJ_SIZE * cos(t), OBJ_SIZE * sin(t));
rotate(t);
image(obj, 0, 0);
pop();
}
// overlapping the transparent gradient buffer
for (let i = 0; i < 20; i++) {
for (let t = 0; t < TWO_PI; t += OBJ_THETA_STEP) {
push();
translate(OBJ_SIZE * cos(t), OBJ_SIZE * sin(t));
rotate(t);
image(img, 0, 0);
pop();
}
}
}
Conclusion
We have explored several ways to achieve a clean annular arrangement with overlapping objects. Now, let's summarize the pros and cons of method.
| Method | Pros | Cons | Suitable case |
|---|---|---|---|
| Redraw only the area where the last object overlaps the first one | Simple to implement | Difficult to handle complex drawings | Ideal for quickly achieving results with simple shapes (e.g., circles). |
| Use an object that allows for an annular arrangement without any overlap | It is an elegant approach because the overlapping is fundamentally avoided | Hard to implement Difficult to handle complex drawings | This technique has the potential to inspire solutions for other problem-solving scenarios. |
| Drawing the portion that would be underneath faintly | The method is highly versatile and capable of handling complex drawings. | Potential for performance overhead | The most practical choice for creative coding that demands a high degree of artistic expression. |
Among all the phases of creative coding, there is a unique joy in the time spent figuring out "How can I make this work?".
There are certainly other unique solutions to this problem. I encourage you to experience the true joy of creative coding: the thrill of figuring out "How can I make this work?" in your own code.
Other solutions
Brilliant approach by @hisadan. The amazing feat of fitting the entire code into a single tweet!
//arcの角度を計算して力技で解決!
— ひさだん (@hisadan) October 30, 2025
float w=200,d,r,p,s=PI/8;
size(800,800);
translate(400,400);
noFill();
for(r=0;r<TAU;r+=s){
d=dist(w*cos(r),w*sin(r),w*cos(r+s),w*sin(r+s));
p=acos(d/w);
arc(w*cos(r),w*sin(r),w,w,p+r-s/2-PI/2,TAU-p+r-s/2-PI/2);
} pic.twitter.com/oeNvRxKkBv












