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.



Then, @senbaku's child solved this problem with the brilliant idea of using half-circles for the first and the last parts.

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
  );

}
 
Solving the overlap problem in an annular arrangement

 

[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?

Two ovelapped half-circles

By connecting these objects sequentially, we can achieve an annular arrangement without any overlap.

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?

Atomic model-like objects
An organic circular object in two colors

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)".

Redraw the problematic area.

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.

Draw object one time using a semi-transparent color.

After 100 iterations of this drawing process, here is the result.

Draw object one hundred times using a semi-transparent color.

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.

With BLUR effect

/** 
 * 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.

MethodProsConsSuitable case
Redraw only the area where the last object overlaps the first oneSimple to implementDifficult to handle complex drawingsIdeal for quickly achieving results with simple shapes (e.g., circles).
Use an object that allows for an annular arrangement without any overlapIt is an elegant approach because the overlapping is fundamentally avoidedHard 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 faintlyThe method is highly versatile and capable of handling complex drawings.Potential for performance overheadThe 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!

Previous Post
No Comment
Add Comment
comment url