The development process for Iterative has not always been just about the math. A significant amount of thought has gone into the aesthetics of the art as well. Let’s take a look at what’s going on under the hood with the outputs!
Initially when I started generating fractal based iterations, I was focused primarily on seeing how far i could push the shapes. More swirl. More depth. More blind dives into the void. At some point I snapped out of the trance, and remembered that form, color, and composition are all equally important.
Initially I was focused on keeping things as simple as possible, using both basic color values, and keeping HSB (Hue, Saturation, Brightness) values straightforward. Why HSB values though, and not something more simple like RGB? As an artist, I think this format is beneficial for a few reasons.
Firstly, HSB is much more closely aligned with how we perceive color; it’s more intuitive. With RGB you are mixing three colors across spectrums to achieve intensity and brightness across hues. With HSB, saturation allows us to modify “purity,” of color, and the brightness of the hue is an independent control, without mashing in other colors.
Secondly, the additive model of 0-255 per color channel is also un-intuitive. Treating color as a 360 degree wheel, and placing saturation value as a 1-100% scale, center-out on the wheel is again, more intuitive. Add the brightness 1-100% scale, and we’re working with a more spatial understanding of color, akin to human perception of reality.
Thirdly, and lastly, we can traverse the color spectrum more gradually, and more harmoniously with this system. Color manipulation in an incremental system is counter intuitive, and furthermore, it feels unnatural. Using percentage as an intensity and brightness modifier alone gives us a more parseable understanding of color and light.
Now that that’s out of the way, let’s look at a sample of the code I am using to manage color outputs. I call hsbvalues(v)
in draw()
, so that the color values (and other modifiers found in hsbvalues()
) can be applied to each point in mandelpoints()
, which we are defining in fractalmath()
(or similar math function), as I outlined in the previous article. Note that we are using v
for each point, which is technically a vector in actuality, hence the letter “v.”
fractalmath() {
{...} // previous code
for (let v of mandelPoints) {
v.mult(650);
}
{...} // following code
}
setup(){
{...} // previous code
hsbvalues(v);
{...} // following code
}
hsbvalues(point){
hv = random(360); // hue
sv = 70 + random(30); // saturation
bv = 80 + random(20); // brightness
fill(hv, sv, bv);
{...} // following code
}
Not all iterations have random values attached to each value, which means not all iterations end up with a “sparkly” effect, and not all iterations that do have a sparkly effect are as intense. The ranges have a lot to do with how the iterations end up feeling.
I’m also keeping color values and other modifiers in hsbvalues()
outside of draw() itself, because I want to ensure that visual properties are dynamically applied and updated in each frame, for each point the loop it iterating over in mandelpoints()
. In short, I’m hitting each point with property updates in each rendered frame, independent of other points.
[img of three iterations - solid color, mid-range random hsb, and intense hsb]
After over a year of sticking with basic primitives (sphere()
to be specific), I had a discussion with quantized (creator of Arttab), and took his advice to heart after experimenting with using different character sets in place of the points. Suddenly, the point clouds had the potential to have an entirely different visual context, by using Unicode characters.
I should take this back a step before continuing to give it even more context. Earlier in 2023 I had become aware of genlight’s work with p5.js. He’s using some of the most quirky and fun characters in his work, instead of regular out-of-the-box primitives. So, being the part-time clown that I am, I was inspired to make this thing:
This piece is, for all intents and purposes, an absurdity where you can toggle between buttplugs and dildos as representations of the vectors present in this specific point cloud. Appropriately, this limited edition is very seriously priced at 0.069 ETH.
Back to Iterative, at this point I moved from having the entire collection have its points represented as spheres, to having a certain percentage of the collection not be represented as such. Thusly, many iterations went from looking one way to another, and the resultant output visibly shifting the visual language of each one.
As I began experimenting with character sets more, I realized that in some cases it added more chaos to the pieces, while in others it created a different kind of calmness. This was in accord with my life-long intention to create art that has the potential to not just make people feel cozy, but also to shake them out of comfort a little bit. Motivated to lean into the juxtapositions of anarchy and codification, I proceeded to build out some character set groupings like so:
//CHARSETS
let cyclical = ['⦿', '◉', '◎', '❂', '●', '✺'];
let reckt = ['■', '□', '▪', '▫', '▬', '▭'];
let pointed = ['◆', '◇', '▲', '△', '▼', '▽'];
let punktual = ['.', '-', '_', ':', ';', '!', '?', '*', '+', '=', '@', '#'];
let sysop = ['␀', '␁', '␂', '␃', '␄', '␅', '␆', '␖', '␇', '␗', '␛', '␡'];
let binary = ['0', '1', 'o', '|', 'O', 'l'];
let unicodeSets = [
{ name: 'cyclical', set: cyclical },
{ name: 'reckt', set: reckt },
{ name: 'pointed', set: pointed },
{ name: 'punktual', set: punktual },
{ name: 'sysop', set: sysop },
{ name: 'binary', set: binary }
];
The naming conventions are mostly inspired by my love of 90s IRC culture, script kiddy bullshit, and code monkeying with a sense of humor. As is the case with Mathcastles’ Terraforms, these different representations of points in space make a huge difference in the visual output. It shifts context visually across spheres of abstraction, metaphor, and even cultural discussions around the significance of glyphs in communication. (I look forward to doing a more esoteric dive into related topics in an upcoming article).
Furthermore, I proceeded to map the characters to points using changing Brightness values. In cases where there is no variation in this value for each frame, there will be no variation of characters as the point clouds progress through time. Here’s an example of how I am doing this with an iteration that is using the reckt
character set, by defining it in hsbvalues()
:
hv = 160 + random(20);
sv = 70 + random(30);
bv = 80 + random(20);
textFont(Noto);
fill(hv, sv, bv);
noStroke();
textSize(33);
let currentUnicodeCharacters = reckt;
let index = floor(map(bv, 50, 100, 0, reckt.length));
let character = currentUnicodeCharacters[index % reckt.length];
Breaking this down, you will note that the brightness value for this iteration is randomly giving us a value between 80-100% in bv
for each rendered frame. In index
, i’m making sure resultant values are being rounded to integers just in case, and mapping values from a range of 50-100% to a range corresponding to the indices of the reckt
array.
This means that the following character array:
['■', '□', '▪', '▫', '▬', '▭']
is mapped to values like so:
'■': 0% to 16.67%
'□': 16.67% to 33.33%
'▪': 33.33% to 50%
'▫': 50% to 66.67%
'▬': 66.67% to 83.33%
'▭': 83.33% to 100%
As a result, we can see the unicode characters ▬
and ▭
flickering in the iteration as frames are rendered. All other characters preceding them fall below the 80% threshold. We are seeing much more of the latter character, as the first one is only happening in the 80-83% range, while most of the bv
output is in the 84-100% range. Here’s a gif of what that output looks like:
The “shimmer” effect is an unexpected effect that is the result of a combination of the HSB values coupled with the characters we are seeing. Coupled with the angle of the point cloud and its rotation values, we end up with a spatial object that is easy to parse, but also rather unique in many ways in each frame that is being rendered. (shh sekrit: some pieces are interactive. With pieces you can click to pause and rotate, hsbvalues()
does not stop rendering per-frame value changes, when the rotation of the overall shape is paused).
The ones with a simple sphere()
still go a long way for me. There is a beauty to the simplicity of a point cloud being made of, well, points.
In the next article, I’m going to get into some of the funkier stuff that’s going on with the interactive elements present in a number of iterations, as well as some other oddly specific attributes. Invariably, a lot of these elements all play into the aesthetics of the art with this project, so while these articles are numbered numerically, I do believe they are not necessarily sequential.
If you would like to play with some geeky stuff, you can poke around my personal sandbox here:
I am of course, once again reminding you, that this project is funded solely by IMP sales (Iterative Minting Pass). Picking one up, ensures you get one iteration from the main collection airdropped on day of mint - in addition to all the airdrops you get pretty much in perpetuity from our sandbox explorations.