Skip to content

Instantly share code, notes, and snippets.

@attentionmech
Created March 4, 2026 12:58
Show Gist options
  • Select an option

  • Save attentionmech/24d8dfba6b0c0d4eca5805d7c64ab62c to your computer and use it in GitHub Desktop.

Select an option

Save attentionmech/24d8dfba6b0c0d4eca5805d7c64ab62c to your computer and use it in GitHub Desktop.
forest scene in p5js
let boids = [];
let trees = [];
let shrubs = [];
let water = [];
let horizon;
function setup(){
createCanvas(900,600);
horizon = height*0.62;
for(let i=0;i<35;i++) boids.push(new Boid());
for(let i=0;i<25;i++) trees.push({x:random(width),h:random(70,120)});
for(let i=0;i<45;i++) shrubs.push({x:random(width),s:random(8,14)});
for(let i=0;i<500;i++) water.push(new WaterParticle());
}
function draw(){
background(18);
drawSky();
drawBoids();
drawGround();
drawWater();
drawTrees();
drawShrubs();
}
/* SKY */
function drawSky(){
noStroke();
for(let y=0;y<horizon;y++){
let c = map(y,0,horizon,40,10);
fill(c,c+20,c+40);
rect(0,y,width,1);
}
}
/* BOIDS */
class Boid{
constructor(){
this.pos=createVector(random(width),random(horizon));
this.vel=p5.Vector.random2D();
this.acc=createVector();
this.maxSpeed=2.5;
this.maxForce=0.05;
}
flock(boids){
let sep=this.separate(boids).mult(1.5);
let ali=this.align(boids);
let coh=this.cohesion(boids);
this.acc.add(sep);
this.acc.add(ali);
this.acc.add(coh);
}
update(){
this.vel.add(this.acc);
this.vel.limit(this.maxSpeed);
this.pos.add(this.vel);
this.acc.mult(0);
if(this.pos.x<0)this.pos.x=width;
if(this.pos.x>width)this.pos.x=0;
if(this.pos.y<0)this.pos.y=0;
if(this.pos.y>horizon)this.pos.y=horizon;
}
display(){
push();
translate(this.pos.x,this.pos.y);
rotate(this.vel.heading());
stroke(255);
line(-6,0,6,0);
line(0,0,-4,-3);
line(0,0,-4,3);
pop();
}
separate(boids){
let desired=20;
let steer=createVector();
let count=0;
for(let other of boids){
let d=p5.Vector.dist(this.pos,other.pos);
if(d>0 && d<desired){
let diff=p5.Vector.sub(this.pos,other.pos);
diff.normalize();
diff.div(d);
steer.add(diff);
count++;
}
}
if(count>0) steer.div(count);
if(steer.mag()>0){
steer.setMag(this.maxSpeed);
steer.sub(this.vel);
steer.limit(this.maxForce);
}
return steer;
}
align(boids){
let neigh=40;
let sum=createVector();
let count=0;
for(let other of boids){
let d=p5.Vector.dist(this.pos,other.pos);
if(d>0 && d<neigh){
sum.add(other.vel);
count++;
}
}
if(count>0){
sum.div(count);
sum.setMag(this.maxSpeed);
let steer=p5.Vector.sub(sum,this.vel);
steer.limit(this.maxForce);
return steer;
}
return createVector();
}
cohesion(boids){
let neigh=40;
let sum=createVector();
let count=0;
for(let other of boids){
let d=p5.Vector.dist(this.pos,other.pos);
if(d>0 && d<neigh){
sum.add(other.pos);
count++;
}
}
if(count>0){
sum.div(count);
return this.seek(sum);
}
return createVector();
}
seek(target){
let desired=p5.Vector.sub(target,this.pos);
desired.setMag(this.maxSpeed);
let steer=p5.Vector.sub(desired,this.vel);
steer.limit(this.maxForce);
return steer;
}
}
function drawBoids(){
for(let b of boids){
b.flock(boids);
b.update();
b.display();
}
}
/* LAND */
function drawGround(){
fill(25,70,35);
rect(0,horizon,width,height-horizon);
}
/* TREES */
function drawTrees(){
for(let t of trees){
push();
translate(t.x,horizon);
stroke(220);
fractalTree(t.h);
pop();
}
}
function fractalTree(len){
strokeWeight(map(len,10,120,1,4));
line(0,0,0,-len);
translate(0,-len);
if(len>12){
push();
rotate(PI/6);
fractalTree(len*0.7);
pop();
push();
rotate(-PI/6);
fractalTree(len*0.7);
pop();
}
}
/* SHRUBS USING L-SYSTEM */
function drawShrubs(){
for(let s of shrubs){
push();
translate(s.x,horizon+5);
scale(0.4);
stroke(180);
lsystemBush(s.s);
pop();
}
}
function lsystemBush(size){
let sentence="F";
for(let i=0;i<3;i++){
let next="";
for(let c of sentence){
if(c=="F") next+="F[+F]F[-F]F";
else next+=c;
}
sentence=next;
}
let len=size;
let angle=PI/5;
for(let c of sentence){
if(c=="F"){
line(0,0,0,-len);
translate(0,-len);
}
else if(c=="+") rotate(angle);
else if(c=="-") rotate(-angle);
else if(c=="[") push();
else if(c=="]") pop();
}
}
/* WATER STREAM */
class WaterParticle{
constructor(){
this.pos=createVector(random(width),random(horizon+40,height));
this.vel=createVector();
}
update(){
let n=noise(this.pos.x*0.003,this.pos.y*0.003,frameCount*0.01);
let angle=n*TWO_PI*2;
this.vel.x=cos(angle)*1.2;
this.vel.y=sin(angle)*0.4;
this.pos.add(this.vel);
if(this.pos.x<0) this.pos.x=width;
if(this.pos.x>width) this.pos.x=0;
if(this.pos.y<horizon+30) this.pos.y=random(horizon+40,height);
}
display(){
stroke(120,170,255,120);
point(this.pos.x,this.pos.y);
}
}
function drawWater(){
for(let p of water){
p.update();
p.display();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment