Link to my work: https://sky-falling.github.io/cc-lab/myfirstwebsite/
Link to my code: https://editor.p5js.org/Sky-Falling/sketches/94ILyEatJ
Image:
Video:
Worm Tree—A newly discovered creature on the red planet
This is an interactive painting where you can plant ‘Worm Trees’ or watch the explosion of meteorolite by clicking the screen. Worm Tree is a scientific creature which lives on a red planet—Crimson Land. While similar to the shape of trees and the texture of worms, they are dictinctive from any living things on earth due to their lack of organic structure like DNA and cell, which intrigues lots of scientists as they believe such lower organism resembles the biological features of early life in the formation of the earth. Thus, they built a rover and ultimately sent it to this red planet to better understand the creature and its surronding. Now, what you are seeing is from the camera of this rover and you can click the brown soil to grow more Worm Tree. While gentle wind blows across the Crimson sea, Worm Tree strives and the beautiful galaxy can be clearly seen on the ground at the day time, the environment become much harsher during night. After pressing 2, you can instead click the sky and see how meteorolite hits the atmosphere and explodes at night. The galaxy, the sea and the creature are all generated by code, revealing how much computer can do in the arts, which is long considered a privilege of human beings. The beauty of this creature and the sky also explain why human continously seek for life outside earth: they want to explore the unknown beauty, stand on an alien land and stare at a different sky.
Building Procedure:
Design and Composition
The reason this interactive painting becomes what it looks like now is a little bit dramatic. At the beginning, what I want to do is raindrops splashing on the surface of water with the rhythm of music. But during my attempt, I found I accidentally achieve an effect more like fireworks rather than raindrops, so I decided to just keep what I have done and added another character which is more suitable to become the creature—worm tree.
Consequently, I need to integrate them into the same story, so I made up a story about the alien planet—Crimson Land and meteoroite impact. To make the story more convincing, I also decided to add a galaxy at the sky on the painting.
Here’s the dramatic part again: I accidentally create a spiral pattern when experimenting about enhancing 3D effect by adding lines. By adjusting some parameters, I suddenly found this is exactly the galaxy I wanted. And the reflection of light on the sea also uses the same techique.
I really want to make the interaction as impressive as possible rather than just click, click and click. However, the obstacle is that the background shouldn’t be refreshed as worm trees are continously being drawn on the canvas, meaning no complicated movement or interaction can be achieved which often require the background to be refreshed every frame. This also leads to my decision of dividing the worm tree and meteorolite collision into 2 different scenes and use a ‘loading page’ as transition, so that I can refresh the page at the second scene and support some more complicated movements like meteorolite impact and floating cloud. Definitely I also alter the details of my story accordingly but still some audience argued it was not convincing enough.
There’re still things I fail to achieve in the process. I previously wanted to make the galaxy rotate, stars twinkle and the reflection of light changes with time. But later I found to achieve that, the position and brightness of each dot need to be stored in array and refreshed every frame, which pose a high demand for the ability of CPU. So I ultimately quit this idea and turn them into static pattern.
In conclusion, this project embraces the key concept of ‘interactive art’, which stress on explore new stuffs in the course of construction rather than set a fixed goal and move towards it without adding new things. My initial goal is to create raindrops splashing effect, but turns out to be something totally uncorrelated but more impressive and stunning. As you can see, almost all components of this painting are kind of by-products of different experiments. So what I have learned is we don’t need to spend too much effort on how to achieve a detail, but do some experiments first, focus more on the what we have achieved and explore if we can turn it into something interesting and intergrate it into the big framework of our projects.
Technical
This complex painting is composed by the following components: Galaxy, star, sky texture and sun, sea and light reflection, land, worm trees, loading page, cloud and meteorolite explosion. Let’s break them down:
Galaxy:
galaxy_x = random(200, 400); galaxy_y = 150 + random(-20, 20); while (millis() < 1500) { push(); translate(galaxy_x, galaxy_y); brightness_sky -= 0.77; rotate(radians(shape * (10 - density) * a)); Scale_sky = y / 400; scale(Scale_sky); strokeWeight(1.5); stroke(240 + random(-50, 0), 221 + random(-50, 0), 224, brightness_sky); line_modified(-25, y, 25, 15); y += 10 - density; a++; pop(); }
The first 2 lines are to fix the position of the center of the galaxy. millis()<1500 is to restrict the size of the galaxy, but will also result in a delay. The logic of the following lines is: draw a small line, rotate the whole galaxy a little bit, then draw another slightly longer line, and rotate the whole a little bit, and repeat this process until 1.5 second pass. 2 additional things are the brightness will decrease with the distance to the center of the galaxy and the ‘line’ is my self-defined modified version which is at below.
function line_modified(x1, y1, x2, scale) { let t = 1; let x = x1; while (x < x2) { line( x, y1 + scale * noise(x * oci, y1), x + t, y1 + scale * noise((x + t) * oci, y1) ); x = x + t; } }
You can see that they have ocillation by noise function.
Sky texture and sun:
noStroke(); let noiseScale = 0.01; for (let i = 0; i < width; i += 2) { for (let j = 0; j < height; j += 2) { if (dist(i, j, 700, 100) > 50) { noiseScale = 0.01; } else { noiseScale = 0.05; } const noiseVal = noise((i + width) * noiseScale, j * noiseScale); const currentColor = map(noiseVal, 0, 1, 0, 1); //fill('#FF0057'); //circle(700,100,100); if (dist(i, j, 700, 100) > 50) { fill(130, 130 * currentColor, 15 * currentColor, 180); } else { fill( 149 - 49 * currentColor, 136 * currentColor, 255 - 21 * currentColor ); } rect(i, j, 2, 2); } }
You can sea the sky are divided into 2 parts: sun part which will be filled with purple color and non-sun part(if (dist(i, j, 700, 100) > 50)) which will be filled with red color. To make them more realistic, I use noise function to make the color change continously with its position.
Star part:
let star_color = [ color(255, 210, 125, 255), color(166, 168, 255, 100), color(95, 56, 129, 150), color(255, 215, 215, 255), ]; noStroke(); for (let i = 0; i < 750; i = i + 1) { let a = random(width); let b = random(298); let c = random(1, 3); fill(star_color[floor(random(4))]); if (dist(a, b, 700, 100) > 50) { circle(a, b, c); } }
This part is to generate 750 points of random x-coordinate, random y-coordinate, random radius and random color(from the 4 colors)
Sea and light reflection:
noStroke();
fill(0, 58, 73); //sea color
rect_modified(0, 300, 800, 200, 1); //draw see
function rect_modified(x, y, a, b, want_wave) {
colorMode(HSB);
strokeWeight(1);
for (let i = y; i <= y + b; i++) {
if (want_wave == 1) {
stroke(4, 67, 60 + (i - y) * 0.2);
} else {
stroke(45, 90, 20 + (i - y) * 0.1);
}
line(x, i, x + a, i);
}
if (want_wave == 1) {
for (let i = 380; i <= 1000; i += 7) {
let wave_brightness = map(i, 380, 550, 75, 100);
stroke(350, 25, wave_brightness);
push();
translate(galaxy_x, 275);
let new_y = i - 275;
let new_x = -2000;
Scale = new_y / 400;
scale(Scale);
while (new_x < 2000) {
oci = 0.2;
line_modified(new_x, new_y, new_x + random(7.5, 15), 1.7);
new_x += random(20, 140);
}
pop();
}
}
noStroke();
colorMode(RGB);
}
You will find I use rect-modified instead of rect(). rect-modified() will generate a rectangle of the given color, but the difference is that the brightness of color will decrease with the y-coordinate so that the brightness of the further part of the sea will be lower. Another difference is that there are some randomly generated modified-lines which will also be streched according to their position, and this will also enhance the 3D effect.
Worm trees:
I will just briefly describe this part due to its complexity.
push(); translate(x0, y0); Scale = (y0 - 275) / 400; scale(Scale); fill(255);
This is to adjust the creature’s size according to the its position to have 3D effect. x0 and y0 is the point where a worm tree starts to grow.
fill(255); for (let i = 0; i < len; i++) { t = millis() - t0; radius = map(t, 0, time_limit, radius_max, radius_max * decay); speed_oci = map( radius, radius_max, radius_max * decay, speed_oci_max, speed_oci_max * decay ); ocillation = map(t, 0, time_limit, oci_max, oci_max * decay); brightness = map(t, 0, time_limit, 250, 200); thickness = map(t, 0, time_limit, thickness_max, thickness_max * decay); threshould = map(t, 0, time_limit, 1, deathrate_limit); strokeWeight(thickness); plant[i][0] += random(-ocillation, ocillation);
This part is to adjust some parameters of the currently growing creature according to time like radius, y coordinate speed and random left-right oscillation.
grow(i);
You can see this function below. This function basically does the following things: 1) At the probability of (1-‘threshould’) which will decrease with time, the branch will stop growing. 2)At the probability of ‘threshould’ and the number of continously growing units for this branch without dividing or stop growing, this branch will proceed growing one more unit. 3)Otherwise, this branch will divide into 2 branches which is achieved by .push() and .splice(). Please notice that the 2 additional branches will have random offset on their x-coordinate speed.
if (t > time_limit || len > maxVal || len == 0) { let location = 0; if ( dist(mouseX, mouseY, 575, 500) < 240 || (mouseX > 600 && mouseX < 800 && mouseY < 500 && mouseY > 300) ) { location = 1; } if (mouseIsPressed == 1 && location == 1 && mouseY >= 0) { x0 = mouseX; y0 = mouseY; location_y_pre = mouseY; tree_color = color(random(360), 100, 30); plant = [[0, 0, 0, speed_max, tree_color, 0]]; len = 1; t0 = millis(); } }
This part is to check if the growth of the current branch is over, like the growth time have surpassed the time_max, all branches have stopped growing or the number of branches is over maxVal. If so, then it will repeatly detect if user click the land and grows another worm tree.
Meteorolite explosion and loading page are both explained in my previous post about generative movement, and cloud is generated in the same way as sky texture. To be honest, I didn’t meet too many techinical problems but did spend lots of time on adjusting parameters, especially those about color, speed and death thredshould. I think probably the only way to figure out the right parameters is to repeatly experiment. And also, I had to abandon some initial ideas in the course of construction. For example, I initially want the land to have more complex colors and shapes. However, I later found that it’s hard to fill something as complicated as ellipse with ‘complicated color’ or achieve the following effect, and this is why the land had a boring color.
If time can be turned back for a week, I won’t build 2 scenes again as Professor Leon told me a way to avoid the refreshment problem. Actually, for some static parts like the galaxy, the sun and the sky texture, we don’t need to refresh them every frame but can instead draw a new canvas and cover it over the main canvas every frame, which won’t cost any calculation on CPU. And thus, I’ll be able to move the meteorolite part and cloud back and probably even make the star twinkle, which also won’t demand too much calculation considering the small number of stars. And I also discover a quite useful tool along the way: colorMode(). I found HSB mode is much better at coping with gradient effect.
Reflection and Future Development
Different from my classmates’ work which are mostly some games, my goal from the begining is to make a dynamic and interactive painting. And I also want to make the creature more automous, meaning user don’t have as much control over it as in games. As the name ‘generative’ creature have indicated, this creature must be able to grow, to eat and to die. I think the ‘grow’ part is quite perfect, as not only the thickness of branch will decrease, but the color will also be dimmer, which are quite in line with human’s aesthetics for tree-like stuffs. The ‘die’ and ‘eat’ part isn’t revealed in the painting but covered in my story about it. However, some audience suggested me reveal how the creatures die on the painting rather just by words and the story about extinction caused by meteorolite is not convincing enough. For the whole sense of this painting, some audience also argued that they even thought the above part and the lower part are two seperate scenes, showing the sea and the sky are too split from each other. Some other problems about details also attract my attention: some audience thought the texts on the land ruin the whole sense of the painting and should be moved to the corner, and others thought simply refresh the whole page when pressing 1 is too boring and should add a loading page instead. All in all, although finally reaching a different theme from my initial proposal—raindrop on water, what I’ve achieved now is still a satisfying painting. Worm Tree can grow on its own, consume minral substance and be killed by meteorlite, satisfying the conditions for generative creature. And all elements, like Crimson Sea, galaxy, sky, the sun and the meteorolite, are all in line with the theme of this painting—alien planet, giving it a sense of harmony but not boring, and these parts mostly come from the inspiration from my previous works. However, I also need to commit some mistakes I made. I agreed with most of the critiques others made, like the sense of wholeness and the unconvincing part of the story. I will employ the simliar method of integrating what I have made rather than spend too much effort on brand new stuffs. What’s more, this project only have 2 scenes, but I will add more in the future and ultimately turn it into a “planet adventure game”.
Credits & References
Professor Leon offered me some advice about how to change color mode and create an additional canvas. So I want to thank him for helping me.
Leave a Reply