I love the works of Japanese artist Yayoi Kusama, and have — for a long time — wanted to make an interactive poster of her famous dots.
Although my father is a retired Math-teacher, I simply cannot grasp the super-complicated "circle-packing" algorithms, so I ended up doing something more simple:
function kusamaDots(
numDots,
minRadius,
maxRadius,
width,
height
) {
const dots = [];
function createDot() {
const x = Math.floor(Math.random() * width);
const y = Math.floor(Math.random() * height);
const radius =
Math.random() * (maxRadius - minRadius) + minRadius;
const dot = { radius, x, y };
if (!dots.some((c) => intersects(dot, c))) {
return dot;
}
return null;
}
while (dots.length < numDots) {
const dot = createDot();
if (dot !== null) dots.push(dot);
}
return dots;
}
Parameters are:
-
numDots
. Total number of dots. -
minRadius
. Minimum radius of a dot. -
maxRadius
. maximum radius of a dot. -
width
of the "canvas" (SVG viewBox). -
height
of the same.
The createDot
method creates a random x
and y
-position within the boundaries of the viewBox
. It sets a random radius between the values of minRadius
and maxRadius
.
It creates a dot
-object, and then it checks whether the dot intersects with any other dots (that have been created already). For this, we need a small helper-method:
function intersects(first, second) {
const dx = first.x - second.x;
const dy = first.y - second.y;
const distance = Math.sqrt(dx * dx + dy * dy);
const sumOfRadii = first.radius + second.radius;
return distance <= sumOfRadii;
}
If the dot does not intersect with any other dots, it's return
ed.
After that, a while
-loop runs until numDots
have been reached.
To output the dots, we simply create <circle>
s in SVG:
const dots = kusamaDots(
e.target.valueAsNumber,
10,
150,
1000,
1000
);
svg.innerHTML = dots
.map(
(dot) =>
`<circle r="${dot.radius}" cx="${dot.x}" cy="${dot.y}"></circle>`
)
.join("")
CSS
The CSS is fairly simple. The frame and responsive texts are similar to The Moon in 10241 Dots.
The most important part is a custom property used for the dot color (as fill
)!
However, I decided to do a custom range slider and a color-picker as part of the poster, thus making it an interactive poster:
Can you spot them?!
Drag the slider and click the color-picker to make your own poster:
Demo
Top comments (4)
Well done once again! Am very happy to subscribe to your posts.
I've also had to face the circle packing challenge a few months ago - but with balls in/on a cylinder in 3D.
I realized that the best solution would be some kind of data structure that represents the remaining available surface coordinates, and remove a chunk of that surface every time a ball is added.
Sadly, turns out this was very CPU consuming, at least with the libraries that I found. I ended up implementing something similar to the approach that you took, which is kind of silly, and not optimal, but good enough.
Thanks — happy to hear you like the posts! I went with the simplest "packing"-solution in the end, which fits Yayoi Kusamas style well, but is not ideal for grouping (and sorting) circles — like the more complex packing-algorithms.
Thanks for sharing. Something about mixing code with visual arts makes it feel more fun!
Indeed it’s fun!