Generative iris
4 minutes read | 804 words by Ruben BerenguelHere are the details about how my procedural sketch Iris works. This has been one of the fastest and most enjoyable sketches I have created lately
One day I woke up and thought to myself: I should do a generative iris sketch. I don’t remember why.
I added it as an idea to my list of p5js sketches to create, and last Wednesday while waiting for ibuprofen to work (I had a two-days-and-counting headache), started with it. In just two short afternoons I had a result I am very happy with.
There are several things that go into thinking “I will program this”. First, the question: is it doable? In this case, since I knew it could be drawn “by humans”, was yes. The next would be how would I do it?. To prepare, I watched this Youtube tutorial on how to draw an eye with Procreate. This gave me the structural ideas I needed: if you don’t know how you’d draw/paint/construct the object, creating it with code is very hard.
I started by implementing a solid circle with a blurred border. Pretty much the same I did for the moonlight and halo in Crazy Nights.
With this, I could have an iris (although flat) and a pupil (with not a lot of interest).
The next would be adding some pattern to the iris. Irises have a linear (muscular fibres, if I’m not mistaken) pattern flowing from the pupil outside. For my first implementation, I wrote a generator of lines between the pupil and the outer iris. This method would generate N
(for a large N
) lines of length adjusted by Perlin noise in a radiating shape. I used a similar approach for the pupil. This is because the pupil is not perfectly circular, but has a ragged contour (because the iris is a muscle).
The generation of lines is parameterized by a random position (as a percentage) between the outer pupil and the outer iris and a percentage of size to fill for whatever is available until the borders. So, for example, if the initial position is at 0.5 (50%, or middle), a 1 of size (100%) would generate lines that would go up to a maximum of full span of the coloured area of the iris. If initial position is at 0.9 (so, almost at the outer border), 100% size would just fill a band of width from 80% of the iris to 100% of the iris.
This was a good start, looked pretty decent for a quick coding session. I added some highlights: primary, secondary and pupil. The pupil one is fake: real pupils don’t shine, the pupil is a hole. But the image looks so much better with it I don’t care that much. The highlights are just a radial set of lines with very low opacity. In this first model, opacity decreases to the borders, but is constant along the radii.
This muscle fiber model looks decent for relatively low resolutions, but on zooming in, it is clear it is just meh. Better solution: with the same approach, construct polygons and contours. The difference in code is small, the different in results is significant. You can zoom in on this with no issues, it just looks good. The old code with lines is still present, though: combining both gives the best results. I also looked for a good set of colours for irises. Still would need a bit of tweaking, some of the greens feel too green.
After this change (and a similar one in the pupil) I tweaked the highlights to be smoother. It’s hard to get really smooth “weird” shapes in p5js
. In the end I tweaked the sizes and numbers manually until it looked good enough. Alpha jagging is unavoidable with this approach. An idea I’ve been floating is using polygonal shapes similar to these arcs but slightly wobbly: then you’d think the alpha jagging combined with the uneven borders were just an artifact of the iris itself. It’s an idea to explore, but I consider the sketch finished as is.
I randomised the start angle of the discs for the fibers, and added some seed to perlin noise. Finally, added random, small, ellipses strewn over the iris. Irises have this kind of randomness.
A happy coincidence with the closing of the polygons (using curveVertex
means you need at least 4 vertices, and repeating the last vertex to make sure it closes… I forgot ¯\_(ツ)_/¯
) makes it look more jagged than it should if the code worked, but the result turns out better by it.