The (digital) art of growing vines

Growing vines in Processing

Danielle H
8 min readSep 1, 2023
How does your garden grow?

In the world of digital art and technology, the Processing programming language is a versatile tool for artists. In this article, we’ll explore how to create digital artwork resembling growing vines with flowers and leaves using code. Let’s create a digital garden.

Processing, in their own words, “promotes software literacy within the visual arts and visual literacy within technology.” It is relatively easy to get started and is capable of stunning complexity. In order to run, it has a setup function that runs once, and a draw function that runs every frame. The code below assumes you have basic knowledge of Java.

Full Processing code is in Gitlab:

p5Js version:

Watch it live here.

I also embedded it into a music video here.

Vines

The concept of creating a vine is to draw a circle; move a bit; then draw a circle again; and so on.

As I wanted the vines to get smaller and smaller, the radius of the vine needs to get smaller from iteration to iteration. I wanted a color of blue-green, so I have values b and g — we will randomize them later. And also, we don’t want to draw each frame, or it will move too fast for us. So we define growth speed.

Vines growing up

class Vine {
//position of current circle
float x;
float y;
//radius of current circle
float rad;
//direction of growth
PVector dir = new PVector(0,-1);
//color
int g = 100;
int b = 100;

//growth speed
long previousTime = 0;
long currTime;
int delta = 10;//milliseconds

//create new Vine at position (x,y), growing at the given angle, with start radius of rad and color (0,green,blue)
Vine(float x, float y, float rad) {
this.x = x;
this.y = y;
this.rad =rad;
}

//are we done growing? (radius too small)
boolean done() {
return rad < 1;
}

//draw and update
void draw() {
currTime = millis();
if (currTime-previousTime>delta) {//draw if time passed
previousTime = currTime;

//draw new circle
fill(0, g,b);
stroke(0, g, b);
circle(x, y, rad);

//update position and radius of next circle
x = x + dir.x;
y = y + dir.y;
rad = rad + random(-0.4, 0.2);
}
}
}

And in the main file:

Vine v;
long prevTime = 0;
long currTime = 0;


void setup() {
size(800, 500);
background(0);
//create a vine at a random position in the lower part of the screen
// initial radius is 30
v = new Vine(random(10, width-10), float(height-30), 30);
}

//draw all vines, flowers and leaves, and spawn branches
void draw() {
currTime = millis();
//draw vine
v.draw();
//when the vine has finished, exit
if (v.done()){
exit();
}
}
Vine going up

This results in a vine that grow upwards, which is what we wanted, right?

Not really.

First, the color is a bit … boring. Let’s randomize the greens and blues a bit. Here and in all following code blocks, added code is marked with <--.

Colorful vines growing up

class Vine {
...
//color
float g = 100; //<--
float b = 100; //<--

//create new Vine at position (x,y), growing at the given angle, with start radius of rad and color (0,green,blue)
Vine(float x, float y, float rad) {
this.x = x;
this.y = y;
this.g = random(255); //<--
this.b = random(255); //<--

this.rad =rad;

}

//are we done growing? (radius too small)
boolean done() {
return rad < 1;
}

//draw and update
void draw() {
currTime = millis();
if (currTime-previousTime>delta) {//draw if time passed
previousTime = currTime;
//update color <--
g += random(-10, 10);
g = constrain(g, 100, 255); //don't want it too dark

b += random(-10, 10);
b = constrain(b, 100, g); // don't want it too blue

//draw new circle
fill(0, g,b);
stroke(0, g, b);
circle(x, y, rad);

//update position and radius of next circle
x = x + dir.x;
y = y + dir.y;
rad = rad + random(-0.4, 0.2);
}
}
}

And now it’s much better:

Live vine going up

Next, the movement is not very natural. Vines should curve a bit. Let’s add a sine to the position of the next circle.

Wavy vines growing up

//draw and update
void draw() {
currTime = millis();
if (currTime-previousTime>delta) {//draw if time passed
previousTime = currTime;
//update color
g += random(-10, 10);
g = constrain(g, 100, 255);

b += random(-10, 10);
b = constrain(b, 100, g);


//draw new circle
fill(0, g,b);
stroke(0, g, b);
circle(x, y, rad);

//calculate durection of next point
//assuming we are drawing a sine wave on the y axis
dir = new PVector( 2*sin(0.03*(y-height)), -rad/3); //<--

//update position and radius of next circle
x = x + dir.x;
y = y + dir.y;
rad = rad + random(-0.4, 0.2);
}
}

That looks more like it :)

Live vine curving

Many wavy vines

Let’s add more vines to our drawing:

Vine v;
long prevTime = 0;
long currTime = 0;

ArrayList<Vine> vines = new ArrayList<Vine>(); //<--

void setup() {
size(800, 500);
background(0);
initVines(); //<--
}

//add new vine at random location on the x axis at the bottom of the screen
//starting radius is 20
void initVines() { //<--
vines.add(new Vine(random(10, width-10), float(height-30), 20));
}


//draw all vines
void draw() {
currTime = millis();
// new code from here: <--
//draw vines
for (Vine v : vines) {
v.draw();
}

//remove vines that have stopped growing or out of the screen
for (int i = vines.size()-1; i >= 0; i--) {
Vine t = vines.get(i);

if (t.done()) {

vines.remove(i);
break;//no need to remove it again below...
}
if (t.x < -20 || t.x > width + 20 || t.y < -20 || t.y > height + 20) {
vines.remove(i);
}
}

//if no more vines, create new vine
if (vines.size()==0) {
initVines();
}

// to here <--
}

Now we have many vines:

Many vines curving

But… They’re all in the same direction. Let’s add an angle to each vine.

A tangle of vines

class Vine {
...
//growth angle
float angle = 0;

//create new Vine at position (x,y), growing at the given angle, with start radius of rad and color (0,green,blue)
Vine(float x, float y, float rad, float angle) {
this.x = x;
this.y = y;
this.angle = angle; // <--
this.g = random(255);
this.b = random(255);
this.rad =rad;

}



//draw and update
void draw() {
...
//calculate durection of next point
//first assuming we are drawing a sine wave on the y axis
dir = new PVector( 2*sin(0.03*(y-height)), -rad/3);
//and then rotate by the given angle
dir.rotate(angle);
...
}
}
}

And in the main file:

//add new vine at random location on the x axis at the bottom of the screen
void initVines() {
vines.add(new Vine(random(10, width-10), float(height-30), 20,random(-HALF_PI/3, HALF_PI/3)));
}
Curvy vines tangling

Awesome!

Now let’s make them branch out.

Spawning vines

Basically, we want a vine to return a new vine, with a small radius, that starts at the current position of the vine:

//in Vine class

//you can play with starting radius, I liked the look of this
float spawnRad = 5;

//create new branch. Here branches are purpusefully small but you can play with these parameters
Vine spawn() {
if (rad>5) {//don't create if too small, looks weird.
if (x>0 && x<width && y>0 && y < height) {//don't create if out of screen
//randomly create to the left or right
float ran = random(-1, 1);
boolean left = true;
if (ran<0) {
left = false;
}
if (left) {
return new Vine(x, y, spawnRad, random(angle - HALF_PI/2, angle - HALF_PI/4) );
} else {
return new Vine(x, y, spawnRad, random(angle + HALF_PI/4, angle + HALF_PI/2));
}
}
}
//if too small, return null
return null;
}

And in the main class:

//when to spawn branches
int spawnTime = 100;//ms

...

//draw all vines
void draw() {
currTime = millis();
//draw vines
for (Vine v : vines) {
v.draw();
}

//spawn branches <--
if (currTime-prevTime > spawnTime) {//play with this for branching speed
prevTime = currTime;
if (vines.size()>0) {
Vine t = vines.get(0).spawn();
if (t!=null) {//t is null if parent branch is too small or out of screen
vines.add(t);
}
}
}

//remove vines that have stopped growing or out of the screen
for (int i = vines.size()-1; i >= 0; i--) {
Vine t = vines.get(i);
if (t.done()) {
vines.remove(i);
break;//no need to remove it again below...
}
if (t.x < -20 || t.x > width + 20 || t.y < -20 || t.y > height + 20) {
vines.remove(i);
}
}

//if no more vines, create new vine
if (vines.size()==0) {
initVines();
}
}
Now we’re talking

If you want a full explanation on the flowers and leaves, please let me know in the comments and I’ll write up an additional article. In the meantime, the full code is below:

Full Processing code is in Gitlab:

p5Js version:

If you are on Linux you can also use it as a screensaver using WebScreenSaver and this link.

And you can watch it live here. But beware — it’s hypnotizing :)

--

--

Danielle H

I started programming in LabVIEW and Matlab, and quickly expanded to include Android, Swift, Flutter, Web(PHP, HTML, Javascript), Arduino and Processing.