In the last two articles, I went over the fundamental math and visual aesthetics in Iterative’s code. In this third article, I will touch on some of the more rare attributes that are found across iterations in the collection.
I think it’s important to note that since we are working with generative code, and not dealing with hats, glasses, shirts, facial features, etc that define so much of the jpeg zoo, we have an opportunity to work with “traits” that don’t necessarily have visual counterparts. Maybe all the unicode triangles in the live sketch below are just hats?
There are a lot of things going on under the hood that contribute to the final output of the scripts. The existence of variables that don’t have a 1:1 correlation to visual output are at the center of this. For example, variables like maxIterations
, increment
, and even order
of magnitude technically translate to traits, and yet, in and of themselves they have no direct real world visual representations; they’re just integers by themselves.
Keeping that in mind, let’s take a look at some of the remaining “traits” that we have not covered, which I secretly hope we can start referring to as something else, since the word trait is most commonly used in reference to characteristics or qualities specific to humans physical and psychological aspects.
The rarest variables found throughout the collection are those related to user-interactive functions. There are three specific kinds: Drag, Zoom, and Randomize.
First up, we have functions present to allow the user to “grab” and “drag” the 3 dimensional point cloud around, which results in an interactive rotation effect.
We are using angleOffsetX
and angleOffsetY
to store the cumulative rotation offset. We are then also storing vales for the mouse’s previous/last position with lastMouseX
and lastMouseY
. Lastly, we are ensuring that dragging
starts with a false value with let dragging = false;
. This seems obvious, but it’s important to note that without either checking whether the iteration has a “Drag” trait, or clicking and dragging the object in the viewport around, the user won’t know if it’s draggable or not.
Following is the code for the functions handling the mouse button staying pressed:
function mousePressed() {
lastMouseX = mouseX;
lastMouseY = mouseY;
dragging = true;
}
function mouseReleased() {
dragging = false;
}
mousePressed()
sets lastMouseX and lastMouseY to the current mouse position and toggles dragging to true. And on release, mouseReleased()
sets dragging back false, releasing the entire shape from the user’s grip.
And here is the function that actually does the math to allow the user to rotate the object, once it’s in their grip:
function mouseDragged() {
let dx = mouseX - lastMouseX;
let dy = mouseY - lastMouseY;
angleOffsetY += dx * 0.5;
angleOffsetX += dy * 0.5;
lastMouseX = mouseX;
lastMouseY = mouseY;
}
It essentially freezes the rotation values for the overall shape, but it should be noted that it does not suspend the rotation, or HSB variations that may be occurring in the iteration.
Here, mouseDragged()
calculates the difference (dx
, dy
) between the current and last mouse positions, adjusts angleOffsetX
and angleOffsetY
accordingly, and then updates lastMouseX
and lastMouseY
. I did divide the offsets in half, because I wanted to mellow out the sensitivity of the mouse motions. At full value, it felt like I was zipping through rotations too fast.
Now all of this does nothing unless we’re applying it in draw()
, so we simply call it all in there like this:
rotateX(angle + angleOffsetX);
rotateY(angle + angleOffsetY);
Within draw()
, if dragging = false
, the angle is incremented to rotate the visual automatically. If dragging = true
, rotateX(angle + angleOffsetX)
and rotateY(angle + angleOffsetY)
apply the rotation based on the user’s mouse movement. This way, the user gets access to an element of control in real-time, using their mouse as a physical interface for rotating the entire point cloud. A whole bunch of code for a clean and minimal effect.
The second interactive effect is also relatively simple, and acts as one would expect with a mouse wheel; wheel-scrolling allows the user to zoom in and out of the the point clouds are being rendered in.
By setting camZ=1500
, the initial camera Z-axis position is established, and then we use zoomSpeed
to set the travel speed, and constrain the minimum and maximum zoom bracket with minZoom
and maxZoom
. Very straightforward stuff.
The function handling the wheel event it self is as follows:
function mouseWheel(event) {
camZ += event.delta * 0.3;
camZ = constrain(camZ, minZoom, maxZoom);
return false;
}
Very straightforward as well. Here, event.delta
adjusts where the camera’s Z-axis position is, with a scaling factor of 0.3 for sensitivity. Additionally, we want to keep camZ within the bracket we established earlier, so the user doesn’t accidentally zoom too far back, or too far forward past the shape into the void. Not the kind of “getting lost” we want for the user experience here.
To wrap this less-is-more interactive element, we allow this interaction to influence and adjust the viewer’s distance by calling `camera(0, 0, camZ, 0, 0, 0, 0, 1, 0)
in draw()
. Being able to explore something at a chosen distance is incredibly simple, and has a great impact on the visual narrative of a piece.
The Randomizer interaction is actually more of a math re-factorizer. Or, maybe it’s more of a re-calculator. Whatever we call this thing, it’s essentially “randomizing” the math so that the visual output changes dramatically every time it is instantiated.
It’s really only utilizing one primary variable: mI
, which we initially set to = 1
. As this number changes, mI
serves as the multiplier to influence visual parameters - specifically, mathematical values determining the shape of the point cloud.
Here’s the function handling the actual user interaction:
function mouseClicked() {
randomSeed(Date.now());
mI = (random(0.003, 1.000));
nonfractalmath();
}
I think that the thing that makes this function pretty cool, in addition to modifying the shape visually, is that a new random seed is generated based on the current time, each time. Essetially, Date.now()
sets mI
to aa new random value within a specified range, and then calls nonfractalmath()
to apply these chages.
In effect, we use this variable to modify the math that is generating the overall point clouds. In a case where we’re in a non-fractal iteration, it would look something like this:
nonfractalmath() {
...
itx = log(0.03) * mI;
ity = 14 * mI;
itz = 3 * mI;
...
}
Wherein changes to mI
modify the mathematical parameters behind what one is seeing rendered. Specifically, once the user clicks on the canvas, the value of mI
multiplies the values for itx
, ity
, and itz
which in turn modify the scaling factor within trigonometric functions determining spatial distribution and orientation of points. TL;DR: it changes the shape.
These three relatively simple interactive elements can have a drastic impact on the visual output, and user experience. But they also touch on the potential of what can be done with interactive sketches.
In fact, there are non-interactive components in the iterations as well, that modify the output based on user input on a totally different level. The easiest example for this, is the current price of Ethereum.
In the Randomizer variable, the user directly impacts the visual output, mostly at random. By interacting with the Ethereum network, a user is technically also modifying the visual output, simply by contributing to the current financial value of Ethereum itself.
So whether we are introducing external factors, adding new features, or building on existing functions, any iterations have the potential to become more than what their initial starting points are. And that is a discussion for another article, where I can lay out what the concept of a "mutable” NFT is, in the context of Iterative.
The next article will hash out what we are doing with our contracts, the significance of how they are laid out, and why it is important that we do what we’re doing on the EVM itself. Oh, and also, how on chain Iterative is, and what that even means!
Meanwhile, pick up an Iterative Minting Pass, and ensure you get an iterations airdropped on mint day!