# it> dev_part_1.txt

By [iterative](https://paragraph.com/@iterated) · 2024-01-19

---

There’s a weird thing that happens when you are really into something, and you start to try and explain it to others: it gets really difficult to do so.

It makes so much sense to you in your own head, and yet, is a total challenge to communicate clearly. In fact, the more complex the thing is, the more complicated it gets to put it into words - that is unless, you are sharing it with folks who are also into it like you are, and share in the thing’s language.

In this article I’m tossing that concern entirely to the wind, and just getting into the nitty gritty of the code and math behind Iterative. This will be an unbridled dive, so some knowledge of math may be helpful. If you’re into JavaScript and math, this may be repetitive, but I hope that it is at least informative. This will all hopefully be of interest to devs.

![sandbox_zen](https://storage.googleapis.com/papyrus_images/4bf8a8960f5e7edec0b4fc0db63e4ef1b0f4de1f71d84e08d70d74ee44156a89.png)

sandbox\_zen

For those folks that have been along for the ride so far, you have an idea of what the art looks like. The [Sandbox airdrops](https://opensea.io/collection/iterative-sandbox) for IMP holders contain almost all of the fundamental code that will be in the primary collection of 1111 Iterations.

So what’s actually going on under the hood? Let’s start by dissecting the math and code in the latest airdrop, [sandbox\_drive](https://gallery.manifold.xyz/0x7fcf0de143afc96980bb0a232595dbed7d70dcc6/8).

Part 1 - Fractal Math
---------------------

In the `fractalmath()` function I am using the [Mandelbrot set algorithm](https://en.wikipedia.org/wiki/Mandelbrot_set) to generate 3D fractals. One of the most important things to note here is that I am converting Cartesian coordinates into spherical coordinates to achieve 3D outputs using webGL (more on this later). These shapes are generally known as [Mandelbulbs](https://en.wikipedia.org/wiki/Mandelbulb).

![left: p5.js point cloud Mandelbulb by me | right: ray traced Mandelbulb by Ondřej Karlík ](https://storage.googleapis.com/papyrus_images/f03fb76e12107f9034e665dd1d28d1a8b3296254698b43de7f4d0a910d0cf7d8.jpg)

left: p5.js point cloud Mandelbulb by me | right: ray traced Mandelbulb by Ondřej Karlík

### Variables

**First, we declare a few integral set variables:**

    const maxIterations = 66;
    const order = 6;
    const inc = map(ethPrice, 1800, 6000, 0.07, 0.045);
    const bail = 66;
    

`maxIterations` determines the maximum number of iterations for the fractal to loop through. In the context of fractal generation, this value controls how deep the algorithm goes before stopping and determining the detail of the fractal. While higher numbers can imply more detail, some variables can negate some of the details. For example, low bail values can cause the algorithm to terminate before all additional iterations can be revealed (this will be very important at a later date for this project, since we are creating modifiable iterations…).

`order` refers to the degree of the equation used in generating a fractal. Another way of wording this would be to say that it effects the shape and complexity of the final geometric object being created. Especially with Mandelbulbs, this value will determine how wildly different the resultant shapes we are rendering can be.

`inc` stands for _increment_, which does exactly what the word means; its value determines the level of detail the fractal will have visually. Think of it as a granularity modifier - as we step through the coordinate space of the fractal, the inc value defines the distance between each point along the way. So if the number is lower, we get multiple points between A → B. If the number is higher, then we get less points between A → B. Lower value = more points in the rendered point cloud.

`bail` is important here, because we want the fractal generation to literally _bail out_ after a certain point, since fractal equations would essentially keep looping to infinity if we let them. This value essentially turns our math into what is known as an _escape-time fractal algorithm_, which means the fractal shape has a boundary. (We also don’t want to turn your silicon into a toaster).

### Iterating

**Next, we nest three** `for` **loops, over a 3D grid** (with `x0`, `y0`, and `z0)`:

      for (let z0 = -1; z0 <= 1; z0 += inc) {
        for (let x0 = -1; x0 <= 1; x0 += inc) {
          let first = true;
          let last = false;
          for (let y0 = -1; y0 <= 1; y0 += inc) {
            let x = x0;
            let y = y0;
            let z = z0;
            let iteration = 0;
    

Within a range of -1 to 1, we increment with the set value of `inc`. In doing so, we are having the setup scan for a cubic location, point by point by point. Booleans `first` & `last` act as flags to track beginning and ending positions later in the code. In the innermost loop here, `x`, `y`, and `z` are initialized to current grid point values at `x0`, `y0`, and `z0`, while `iteration` is initialized to zero - all of which serves to prepare the starting point for the actual fractal calculations.

![high bail, low order, simple math | is that a rug?!](https://storage.googleapis.com/papyrus_images/958a256b543a68537c40898c8b8f735bb30e1be77ba1f6595134d3ca82f8c3ad.gif)

high bail, low order, simple math | is that a rug?!

### Fractal Calculation

Now that we have set up the space to iterate through, **we begin the computation of the fractal:**

            for (; ;) {
              let xx = x * x;
              let yy = y * y;
              let zz = z * z;
              let r = Math.sqrt(xx + yy + zz);
              let theta = Math.atan2(Math.sqrt(xx + yy), z);
              let phi = Math.atan2(y, x);
              let powr = Math.pow(r, order);
              let sinThetaOrder = Math.sin(theta * order);
              let cosThetaOrder = Math.cos(theta * order);
    

We’re firing off an infinite loop with `for (; ;)`, which essentially is the kick off for the computation, but also the thing we will have to break later with defined conditions, so that things don’t explode. This section here is also where we set up conversion from Cartesian to spherical coordinates. **Wait, what the hell does that mean?!**

> > OK, so for those new to, late to, or slept through trigonometry class, the Cartesian coord system represents points in space using orthogonal axes (x, y, z), while the spherical coord system uses a radius and two angles to describe a point's position relative to a central origin.
> > 
> > Here’s another way to express it: think of a grid on a flat map, and you use straight lines to show where any point on it is, by going across, and then up. WIth the spherical coord system, you’re inside a globe, and you draw straight lines from the center of it to a point, and then _spin_ the entire globe vertically and horizontally to arrive at the desired location. Think of latitude and longitude on a map, but your first coordinate is somewhere along the depth of the earth’s layers. Visual aids below:

![left: Cartesian Coordinates | right: Spherical coordinates](https://storage.googleapis.com/papyrus_images/9227fb3da961ba4d8fc20d25d611543bf0ba267f3bc428777a25c8485d6118d2.jpg)

left: Cartesian Coordinates | right: Spherical coordinates

Now that that’s out of the way, let’s take a look at the math functions in the script that are computing the fractal by way of this conversion. (To get a basic understanding of the [2D Mandelbrot set algorithm](https://en.wikipedia.org/wiki/Mandelbrot_set), and the more complex [Mandelbulb equations](https://en.wikipedia.org/wiki/Mandelbulb), be sure to look up the associated wiki pages, because there is a character limit on these articles and I’m not about to get into that specific math.)

**Let’s convert the coordinates:**

1.  Using `xx = x * x; yy = y * y; zz = z * z;`, we compute the square of each Cartesian coordinate.
    
2.  Using `r = Math.sqrt(xx + yy + zz);` we calculate the radial distance ($$r$$), which is equivalent to the magnitude in spherical coordinates.
    
3.  We then define `theta` and `phi` to convert the Cartesian coords to spherical angles \*$$θ$$ \*(polar angle) and \*$$ϕ$$ \*(azimuthal angle)
    

**Let’s apply the Mandelbulb transformations:**

1.  This here `powr = Math.pow(r, order);` raises the radial distance to the power of `order`, which we defined earlier. This is analogous with the radial element of the Mandelbulb equation.
    
2.  And then we set up some shorthand for `sin(theta * order)` and `cos(theta * order);` which we will use to apply the Mandelbulb transformations to the angles. Do note here that we are using `order` again, to scale the angles $$θ$$ and $$ϕ$$ (polar and azimuthal), and i’m not actually using the shorthand variables in this specific sketch (though they are used quite commonly throughout most sketches in the collection).
    

### Variations

**So that’s nice, we can create basic Mandelbulbs now.** However, Iterative is not about exploring the basics; it’s about a spatial journey into discovering new shapes, and iterating through variations of them. Variations on variations on variations… In this particular iteration, we have this math here (using 2500 as the price of eth to simplify the blob):

               x = powr * Math.pow(2500, 3) * Math.sin(order * theta + Math.PI / 2) * Math.cos(theta) * random(1, 2500);
               y = powr * Math.pow(2500, 4) * Math.sin(order * phi + Math.PI / 2) * Math.sin(phi) * random(1, 2500);
               z = powr * Math.pow(2500, 5) * Math.sin(order * (theta + phi)) * Math.tan(theta + phi) * random(1, 2500);  
    

Here, the `pow()` function is raising the value to a power of 3, which will exponentially scale the dimensions of the fractal, which also means its complexity also increases.

We are also multiplying `powr` (the radius raised to the `order`) with the above value, before applying the trigonometric transforms with `theta` and `pi`. This essentially results in a pretty wacky warping of the fractal along these pre-defined axes.

The increment in power (`pow()`) across x, y, and z, ensures that we end up with a non-symmetrical Mandelbulb. `z` is also a little different because it’s combing `theta` and `phi` into a single trigonometric term, which makes a really weird interaction occur between the two angles.

I did add a `random()` factor at the end of these in this specific iteration, as I want the price of Eth to really distort the f\*ck out of the fractal shapes in non-uniform ways.

> > Across the 30 different _types_ of iterations I have created, there are 30 different core variations occurring, although there are a number of iterations that are non-fractals, that still observe similar trigonometric rules, and a number of hybrids, that mesh the two together. Almost all of the 30 types are very different.

![A symmetrical non-fractal that shares some of the math regardless](https://storage.googleapis.com/papyrus_images/0f97f442f1c2d5ffeb37e0c2553bd361efb4c05acea916645fc2fbc588cbb183.gif)

A symmetrical non-fractal that shares some of the math regardless

### Positioning & Conditions

**Next up, we have** **the algorithm perform iterations _and_ some very important checks for conditions**, in order to establish points belonging to the fractal we are creating.

              x += x0;
              y += y0;
              z += z0;
              iteration++;
              const p = new createVector(x0, y0, z0);
              if (iteration > maxIterations) {
                if (first) {
                  mandelPoints.push(p);
                  first = false;
                  last = true;
                }
                break;
              }
              if (xx + yy + zz > bail) {
                if (last) {
                  mandelPoints.push(p);
                  last = false;
                  first = true;
                }
    

Following the previous equations that are doing all the shape discovery, we now proceed to update the coords for `x`, `y`, and `z` by adding the original grid points at `x0`, `y0`, and `z0`. This repositions the transformed point back relative to its original grid location. From here, we can increment the iteration counts for each loop with `iteration++`, which tracks how many times the loop has run for the current point. And to make sure we know which way things are going, we create a vector with `p = new createVector(x0, y0, z0);`, wherein p is the current grid point in 3D space.

Now that we’ve adjusted the position, we can move on to checking conditions we defined earlier. We take a look at whether the iteration count has exceeded the max value we set earlier (`maxIterations`) with `if (iteration > maxIerations)'`. And if it has, and it’s the _first_ iteration that has done so (`` `first = true` ``), we add `p` to the `mandelPoints` array we are creating. Yep, our `first` and `last` flags are finally getting updated, and we’re setting some boundaries.

Now we’ve also gone and created `mandelPoints` here, which is very important in many regards. Before I explain what that does, let’s wrap up the condition checks. The second condition check with `if (xx + yy + zz > bail)` checks if the squared distance from the origin exceeds `bail`, the thing preventing everything from exploding. Now if the distance does indeed exceed the limit _and_ the point we’ve just established is the last one after having done so (`last = true`), it’s added to the `mandelPoints` array, and the flags are updated accordingly.

At this point we `break` to terminate the innermost loop so we can move the computation to the next point in the grid. All of this to draw a single point! Again, and again, and again, and…

![A hybrid with a character set instead of primitives representing the points](https://storage.googleapis.com/papyrus_images/09f7aa1b10a27960475f9ff15eae0334657c8f0760a6008fb685feb9ce72ebc0.jpg)

A hybrid with a character set instead of primitives representing the points

### Collection & Utilization

And so, for the myriad of points we’re discovering across the shape we have created, `mandelPoints` **plays the absolutely crucial role of storing the points that we are establishing as being part of the Mandelbulb we have created**. It is the algorithm’s storage unit, and all of its contents will help us draw the shape later on. In fact, we can use this array to do literally anything with the points it stores, from moving individually indexed points around, to changing what represents them (characters or primitives like spheres), as just a couple of examples. We are the captains now, of each point!

      for (let v of mandelPoints) {
        let scale = map(ethPrice, 2000, 6000, 750, 1200);
        v.mult(scale);
      }
      for (let i = 0; i < mandelPoints.length; i++) {
        mandelPoints[i].rotationSpeed = random(0.5, 2);
        mandelPoints[i].rotationOffset = random(360);
        mandelPoints[i].rotationDelay = frameCount + floor(random(0, 100));
      }
    }
    

In this final section of `fractalmath()`, we take each vector `v` in `mandelPoints`, and scale them uniformly with a predefined `scale` value. We’re essentially sizing each point in the fractal here so it can be big or small. In this case, we’re using the price of Ethereum to determine the scale.

Here, we are also passing along some animation properties for each point in `mandelPoints`. A big part of this project is displaying the Mandelbulbs in motion, so that it’s not just trippy to look at, but so we can get a sense of spatiality; the dimension of time allows us to observe 3D dimensional space as it moves, allowing us to better grok what we’re perceiving visually. `rotationSpeed`, `rotationOffset1`, and `rotationDelay` will all come in very handy later when we start having fun in `draw()`.

![4 instances of sandbox_drive, vectors scaling with the price of Eth increasing](https://storage.googleapis.com/papyrus_images/8d1c1f2c9afaf4a47c04f3cbc794f575b40ecccb108664e1ce19e18db328cf55.jpg)

4 instances of sandbox\_drive, vectors scaling with the price of Eth increasing

### More Technicals

In upcoming articles, I will touch on some of the functions around various features, how we’re drawing the shape with various embellishments, interactive elements, how some variables translate to traits in the metadata for the tokens they will be associated with, and more. I will also be sharing an article about the work [Moon](https://twitter.com/TheMoonOfficia2) has been doing with the contracts behind Iterative, which is the other 50% of this project’s dev.

You can view **sandbox\_drive** iterating live on Manifold, here:

[https://gallery.manifold.xyz/0x7fcf0de143afc96980bb0a232595dbed7d70dcc6/8](https://gallery.manifold.xyz/0x7fcf0de143afc96980bb0a232595dbed7d70dcc6/8)

And you can check out the code either in your browser’s inspector, or just check out [the entire p5.js directly on Arweave here.](https://gpsy2qqlr2by4d7xh2hdbxgqmdlb3jcg66rku6qwflytdmctontq.ar-io.net/M-WNQguOg44P9z6OMNzQYNYdpEb3oqp6FirxMbBTc2c/sandbox_drive.js)

And of course, you can check out our [website](https://iterative.wtf) and find your way to our XthatisformerlyTwitterorwhatever or our Discord from there…

[https://twitter.com/ArtofOrb](https://twitter.com/ArtofOrb)

* * *

As a reminder, this indie project is entirely funded by Iterative Minting Pass sales leading to mint. Funds from IMP sales will help fund our contract array being deployed, as well as funding artists involved with the project, architecture enhancements, and more.

Our Iterative Minting Pass doubles as a presale purchase (you get one iteration airdropped from the main collection on day of mint), and triples as a perks booster. By holding an IMP, you get airdrops on the regular from our Sandbox creations, as well as having a say in the direction of the project.You can pick up your own IMP here:

nft://1/0x1cd2a9d968BeB299169cA843Ac2C1EDf8d89327a/?showBuying=true&showMeta=true&extraParams=eyJwbGF0Zm9ybSI6Ik1BTklGT0xEIiwiZXh0ZW5zaW9uQWRkcmVzcyI6IjB4M0I4QzJmRWIwRjQ5NTM4NzBGODI1RGY2NDMyMmVDOTY3QWEyNkI4YyIsImNsYWltSW5kZXgiOiIxMDQ1MjU4NDgwIn0%3D

---

*Originally published on [iterative](https://paragraph.com/@iterated/it-dev-part-1-txt)*
