How I drew a pencil-sketch-like forest with Processing

Drawing a tree using code is a very basic exercise in programming. All well-trained programmers will, on reflection, explain how to use a "recursive function" to draw the structure of a tree. The forest that I have drawn is no exception. It started with the "recursive function." The first version was just a tree made up of a bunch of straight lines. It was stiff and boring. Then I gradually added improvements in several stages: gradual thickness, randomly twisted branches, shades of light, bark texture, etc. until the result looked like a real pencil sketch. You might guess that I probably used 3D drawing, lighting effects, texture mapping, and other complicated tricks to create this work, but in fact I didn't use any of these things; this work is a pure 2D drawing, and does not use any bitmap material. The only drawing tools I used are point(), ellipse(), line(), and simple mathematical calculations which are very basic features in Processing or in other programming-based drawing tools.

In this article I will show some diagrams and code snippets to explain how this work has gradually developed from the original version to the final pencil-sketch like drawing.

Start with a single line
In the very first version, I just draw white trunks on a black background using the line() funtion in Processing. I then randomly create 2 or 3 branches in each recursive call; the thickness also decreases with each call. Usually in Processing you can recursively call pushMatrix() to shift the origin and angle of the current coordinate. This approach could simplify drawing the branch and reduce mathematical calculations, but I thought that would consume a lot of memory stack. Instead I use polar coordinates to calculate the starting and ending position of each branch in global(original) coordinates, and then draw directly on the PGraphics buffer.

Gradual thickness and twisting effects
We now already have the basic form of a tree, but the tree lacks vitality. I began to think about how to make the thickness of the trunk appear smoother. This is not difficult. I divided the original line into several different segments, and then gradually decreased the stroke weight. Also, in order to increase the efficiency of that process, I divided longer branches into more segments, and shorter branches into fewer segments. This balances the drawing process between smoothness and efficiency. Another benefit of dividing a trunk into many smaller segments is that I can randomly change the angle of each small segment to make it bend more like a real tree.


The following code implements the above concept. This is not my actual code, I have simplified it for better understanding.

void createBranch(float startX, float startY, float branchAngle, float branchLength, float branchWeight, int twigSteps, int branchColor){

     //Begin to draw branch
     float twigStartX = startX;
     float twigStartY = startY;
     float twigLength = branchLength / twigSteps;
		 float branchEndWeight = branchWeight * 0.8;
     //Divide each branch into many twigs
     for(int i = 0; i < twigSteps; i++){
          float twigRandAngle = random(-PI, PI) * 0.05;
          float twigRandLength = random(0.5, 1.5);
          float twigEndX = twigStartX + cos(branchAngle + twigRandAngle) * (twigLength *  twigRandLength);
          float twigEndY = twigStartY + sin(branchAngle + twigRandAngle) * (twigLength *  twigRandLength);
          float twigWeight = map(i, 0, twigSteps, branchWeight, branchEndWeight);

          drawTwig(twigStartX, twigStartY, twigEndX, twigEndY, twigWeight, branchColor);

          twigStartX = twigEndX;
          twigStartY = twigEndY;
     //Create sub branches
    if( branchWeight > 1 ){ 
        int branchNum = (int)random(2, 4);

	for(int i = 0; i < branchNum; i++){
            float newBranchX = twigStartX;
            float newBranchY = twigStartY;
            float newBranchAngle = branchAngle + random(-PI/4, PI/4);
            float newBranchLength = branchLength * 0.6;
            float newBranchWeight = branchEndWeight;
            int newTwigSteps = (int)twigSteps*0.9;
            int newBranchColor = (int)branchColor*0.9;
	    createBranch(newBranchX , newBranchY , newBranchAngle, newBranchLength , newBranchWeight , newTwigSteps , newBranchColor);

Lighting and shade
In the beginning each tree I drew had only a single color (brightness). Although we could change the grayscale of each tree to create depth of field, the overall picture was still very flat, so now I started thinking about how to vary the light and shade on each branch. I divide each small segments from left to right into 6 to 8 parallel lines, and then draw these lines with smaller stroke weight, changing the color(brightness) of each stroke. This creates a light to dark effect. In order to increase the efficiency of the process, I divided thicker branches into more parallel lines, and thinner branches into fewer lines.

Bark texture
I first tried to use a bitmap bark texture (which I found on the Internet), but the effect was not good. Instead I decided to continue drawing texture with basic drawing function in Processing. After drawing each small tree segment, within the range of that segment I randomly added some dots of various sizes, both small and large. This approach produces a tree that looks good.


The following code shows how to draw shadows and textures.

void drawTwig(float twigStartX, float twigStartY, float  twigEndX, float twigEndY, float twigWeight, int twigColor){
	//Use coordinate transformation to simplify calculation
	translate(twigStartX, twigStartY);
	rotate(atan2(twigEndY- twigStartY, twigEndX- twigStartX));
	//Draw twig from light to dark	
   	int gradientStep = 6;
	float gradientWeight = twigWeight/gradientStep;
        for(int i = 0; i < gradientStep; i++){
		float x1 = -twigWeight/2 + i * gradientWeight ;
		float y1 = 0;
		float x2 = -twigWeight/2 + i * gradientWeight ;
		float y2 = twigEndY - twigStartY;
		int c = twigColor - (gradientStep/2) * 5 + i * 5;
		line(x1, y1, x2, y2);
	//Draw dots
	 for(int i = 0; i < twigWeight*2; i++){
		float dotX = random(-twigWeight/2, twigWeight/2);
		float dotY = random(0, twigEndY - twigStartY);
		if(random(0, 1) < 0.2){
			strokeWeight(twigWeight * random( .2, .4)); //Draw big dot
			strokeWeight(twigWeight * random( .01, .1)); //Draw small dot
		point(dotX, dotY);

From a tree to a forest
To make my drawing as smooth as possible, I divided a tree into many, many small segments, such that each tree is actually drawn from thousands of small lines and points. On my computer (my device is CPU i7 / GPU 1050Ti ), it takes several seconds to draw a single tree. Now imagine that you have to draw hundreds of trees. Your program will soon get stuck and be unable to respond. In Processing I overcome this problem by only drawing a small segment on each frame. After drawing a tree, I continue to draw another. This avoids the program stuck problem, and I can also see the tree growth as an animation. In fact, my program took about 20 minutes to complete the final work.

Even with the simplest tools you can make amazing results. I hope you can get some inspiration from this article. If you are interested, you can use the Parameter design tool on my website in order to see how these trees are drawn.

Upgrade your browser

You need HTML5 Canvas supported browser to see animated effects