Order and Chaos
Sun Feb 24, 2019 · 1155 words

One can always find a shimmer of order in chaos. And order without chaos is a fallacy.

Order Chaos

I started with generative art by matt pearson and the first lesson was about creating lines on your own, add some variance and noise.

This gave me the idea to create a line with jitter effect. To do so, I had to take a trip down 10th standard maths lane and remind myself equations of lines, parallel lines, perpendicular lines, equation of a circle and its parametric form.

Armed with this knowledge, I laid down the plan to create the jitter effect.

I will randomly choose a point(X1) on the line(l1), find the line perpendicular to the given line(l2), choose a random point(X) based on given maximum height on this perpendicular line, and the point I chose on my original line(X1) will get shifted to this new point(X).

l1                    After shifting 
.                     .
.                     .
.                     .
.                     .
X1 . X . . . . l2        .
.                     .
.                     .
.                     .
.                     .

And after handling some divide by 0 errors and other edge cases, here’s the code I arrived at:

def get_slope(start_x, start_y, end_x, end_y):
	if end_x == start_x:
		return float("inf")
	return (float(end_y - start_y) / (end_x - start_x))

def get_y_of_line(start_x, start_y, end_x, end_y, x, slope = None, y = 0):
	slope_to_use = get_slope(start_x, start_y, end_x, end_y) if slope is None else slope
	y_to_use = y if slope_to_use == float("inf") else -y if slope_to_use == float("-inf") else None 
	if y_to_use is not None:
		return start_y + y_to_use
	return round(slope_to_use * (x - start_x) + start_y)

def jitteryLine(start_x, start_y, end_x, end_y, max_height=3, x_step=1, y_step=1, chance_threshold = 40):
	y = start_y
	x = start_x
	slope = get_slope(start_x, start_y, end_x, end_y)
	perpendicularSlope = -1/slope if slope != 0.0 else float("inf")
	
	start = start_x
	end = end_x - x_step + 1

	if start == end:
		end = end + 1

	if end < start:
		start, end = end, start
	
	for scan_x in range(start, end, x_step):
		next_straight_x = scan_x + x_step
		next_straight_y = get_y_of_line(start_x, start_y, end_x, end_y, next_straight_x, slope)
		
		next_x = next_straight_x
		next_y = next_straight_y
	
		chance = random(90) + 10
		if chance < chance_threshold:
			random_height = random(2*max_height) - max_height
			if perpendicularSlope != float("inf") and perpendicularSlope != float("-inf"):
				next_x = next_straight_x + random_height
			next_y = get_y_of_line(next_straight_x, next_straight_y, None, None, next_x, perpendicularSlope, random_height)

		line(x, y, next_x, next_y)
		y = next_y
		x = next_x

And now that I had a framework to create jittery line, I could create jittery anything, since fundamentally, everything can be created with a line, after all, a circle is a polygon with infinite edges.

Creating a jittery circle was no issue, use the parametric form of circle’s equation and create tiny jittery lines, and when done you would have created a jittery circle.

def jitteryCircle(center_x, center_y, radius, turns = 1):
	x = None
	y = None
	for angle in range(0, (360 * turns) + 1) :
		next_x = int(round(center_x + radius*math.cos(radians(angle))))
		next_y = int(round(center_y + radius*math.sin(radians(angle))))
		if x is None or y is None:
			x = next_x
			y = next_y
			continue
		jitteryLine(x, y, next_x, next_y)
		x = next_x
		y = next_y

One important thing I learnt in matt pearson's book was that once you have a framework, start playing, amplify. And that’s exactly what I did. That turns argument in jitteryCircle controls how many times it should draw the circle. But what’s the point, it would be drawing circles over the previous one, and it would appear as if only one circle is present. But what if, with each turn, radius increased, randomly? What if, with each iteration, the color gradient changed? What if, with each iteration, the jitter effect increased? Well you get the idea. After creating a base framework, we need to ask such bizzare question, and I believe, no question is bizarre enough when it comes to genrative art.

import math

def get_slope(start_x, start_y, end_x, end_y):
	if end_x == start_x:
		return float("inf")
	return (float(end_y - start_y) / (end_x - start_x))

def get_y_of_line(start_x, start_y, end_x, end_y, x, slope = None, y = 0):
	slope_to_use = get_slope(start_x, start_y, end_x, end_y) if slope is None else slope
	y_to_use = y if slope_to_use == float("inf") else -y if slope_to_use == float("-inf") else None 
	if y_to_use is not None:
		return start_y + y_to_use
	return round(slope_to_use * (x - start_x) + start_y)

def jitteryLine(start_x, start_y, end_x, end_y, max_height=3, x_step=1, y_step=1, chance_threshold = 40, iteration = 1):
	y = start_y
	x = start_x
	slope = get_slope(start_x, start_y, end_x, end_y)
	perpendicularSlope = -1/slope if slope != 0.0 else float("inf")
	
	start = start_x
	end = end_x - x_step + 1

	if start == end:
		end = end + 1

	if end < start:
		start, end = end, start
	
	for scan_x in range(start, end, x_step):
		next_straight_x = scan_x + x_step
		next_straight_y = get_y_of_line(start_x, start_y, end_x, end_y, next_straight_x, slope)
		
		next_x = next_straight_x
		next_y = next_straight_y
	
		chance = random(90) + 10
		if chance < chance_threshold:
			random_height = random(2*max_height) - max_height
			if perpendicularSlope != float("inf") and perpendicularSlope != float("-inf"):
				next_x = next_straight_x + random_height
			next_y = get_y_of_line(next_straight_x, next_straight_y, None, None, next_x, perpendicularSlope, random_height)

		line(x, y, next_x, next_y)
		y = next_y
		x = next_x

		stroke(map(noise(iteration), 0, 1, 80 - iteration, 160 - iteration), map(noise(iteration), 0, 1, 0 + iteration, 0 + iteration), map(noise(iteration), 0, 1, 0 + iteration*1.8, 0 + iteration*1.8), map(noise(iteration), 0, 1, 0 + iteration*1.2, 0 + iteration*1.2))

def jitteryCircle(center_x, center_y, radius, turns = 1, iteration = 1):
	x = None
	y = None
	for angle in range(0, (360 * turns) + 1) :
		next_x = int(round(center_x + radius*math.cos(radians(angle))))
		next_y = int(round(center_y + radius*math.sin(radians(angle))))
		if x is None or y is None:
			x = next_x
			y = next_y
			continue
		max_height = map(noise(angle), 0, 1, 3, 8)
		jitteryLine(x, y, next_x, next_y, max_height=max_height, iteration=iteration)
		x = next_x
		y = next_y
		radius += 0.004


background(255)
size(3110, 3110)
stroke(64,167,224)
strokeWeight(1)	
radius = 10
distance = 840

for iteration in range(1, 160):
	strokeWeight(map(noise(iteration), 0, 1, 1, 1.2))
	jitteryCircle(width/2, height/2, radius + map(noise(iteration), 0, 1, radius*iteration*0.7, radius*iteration), 15, iteration=iteration*0.4)

saveFrame("screen-####.png")

If you have worked with Neural Networks, you’d know the concept of weights and how they are set after iterations of learning, and how they look almost random. The numbers above might look like weights which have been set by a complex network after several dozens of iterations. And in fact they have been, it’s just, the complex network is me this time. I sat down and played with the code, rendering after each experiment with different variables and then I chose, what looked to me, like Order and Chaos

I am not sure what the takeaway should be, but I think what I wanted to tell was that I believe the best machine to create art will always be human mind. All we need to do is venture out, in the terra incognita of code and ideas.

There might not be new lands to discover and explore for our generation, but we have something else, something more, something infinite to explore.


back · gallery · projects · about · main