AI Growing Plant - Seed to Flower Journey

This sketch animates a plant's complete lifecycle from seed to full bloom over 10 seconds. The plant grows progressively through five stages (Seed → Sprouting → Growing → Blooming → Full Bloom), with a stem that extends, leaves that appear at different heights, and finally colorful flower petals that bloom at the top. The animation uses time-based progression to create a smooth, satisfying growth visualization.

🎓 Concepts You'll Learn

Animation with timeConstrain functionConditional statementsTrigonometry (cos/sin)Canvas resizingText renderingCoordinate systemsLoops for repetition

🔄 Code Flow

Code flow showing setup, draw, windowresized

💡 Click on function names in the diagram to jump to their code

graph TD start[Start] --> setup[setup] setup --> draw[draw loop] draw --> timeprogress[Time Progress Calculation] draw --> soildrawing[Soil Rectangle] draw --> seeddrawing[Seed Drawing] draw --> stemgrowth[Stem Growth Logic] draw --> stagelabels[Stage Label Logic] stemgrowth --> leafleft[Left Leaf Appearance] stemgrowth --> leafright[Right Leaf Appearance] draw --> flowercenter[Flower Center] draw --> flowerpetals[Flower Petals Loop] click setup href "#fn-setup" click draw href "#fn-draw" click timeprogress href "#sub-time-progress" click soildrawing href "#sub-soil-drawing" click seeddrawing href "#sub-seed-drawing" click stemgrowth href "#sub-stem-growth" click stagelabels href "#sub-stage-labels" click leafleft href "#sub-leaf-left" click leafright href "#sub-leaf-right" click flowercenter href "#sub-flower-center" click flowerpetals href "#sub-flower-petals" draw --> windowresized[windowResized] click windowresized href "#fn-windowresized"

📝 Code Breakdown

setup()

setup() runs once when the sketch starts. Here we initialize the canvas size, record the starting time, and configure text settings. Using windowWidth and windowHeight makes the sketch adapt to any screen size.

function setup() {
  createCanvas(windowWidth, windowHeight);
  startTime = millis();
  textAlign(CENTER, TOP);
  textSize(24);
}

Line by Line:

createCanvas(windowWidth, windowHeight)
Creates a canvas that fills the entire browser window, making the sketch responsive to different screen sizes
startTime = millis()
Records the current time in milliseconds when the sketch starts, used later to calculate how much time has passed
textAlign(CENTER, TOP)
Sets text alignment so that text is centered horizontally and aligned to the top vertically
textSize(24)
Sets the font size to 24 pixels for the stage labels

draw()

draw() runs 60 times per second. This function calculates how much time has passed since the sketch started (using the 't' variable), then uses that progress value to animate different parts of the plant at different times. The key insight is that t goes from 0 to 1 over 10 seconds, allowing us to trigger different visual elements at specific percentages of the animation timeline.

function draw() {
  background(180, 220, 255);
  let soilH = 80;
  noStroke();
  fill(120, 70, 30);
  rect(0, height - soilH, width, soilH);
  let t = constrain((millis() - startTime) / 10000, 0, 1);
  let baseX = width / 2, baseY = height - soilH;
  fill(150, 90, 40);
  ellipse(baseX, baseY - 10, 20, 14);
  let stemMax = height - soilH - 120;
  let stemH = stemMax * t;
  if (t > 0.1) {
    stroke(40, 150, 60);
    strokeWeight(6);
    line(baseX, baseY - 10, baseX, baseY - 10 - stemH);
    noStroke();
    fill(40, 160, 70);
    if (t > 0.3) ellipse(baseX - 25, baseY - 10 - stemH * 0.4, 30, 16);
    if (t > 0.4) ellipse(baseX + 25, baseY - 10 - stemH * 0.6, 30, 16);
  }
  if (t > 0.6) {
    let fx = baseX, fy = baseY - 10 - stemH;
    fill(255, 80, 140);
    for (let i = 0; i < 6; i++) {
      let a = TWO_PI * i / 6;
      ellipse(fx + 20 * cos(a), fy + 20 * sin(a), 20, 28);
    }
    fill(255, 230, 140);
    ellipse(fx, fy, 18, 18);
  }
  let stage = "Seed";
  if (t > 0.1) stage = "Sprouting";
  if (t > 0.3) stage = "Growing";
  if (t > 0.6) stage = "Blooming";
  if (t > 0.9) stage = "Full Bloom!";
  fill(40, 80, 120);
  text(stage, width / 2, 20);
}

🔧 Subcomponents:

calculation Soil Rectangle rect(0, height - soilH, width, soilH)

Draws a brown rectangle at the bottom of the canvas to represent soil

calculation Time Progress Calculation let t = constrain((millis() - startTime) / 10000, 0, 1)

Calculates animation progress from 0 to 1 over 10 seconds, constrained so it never goes below 0 or above 1

calculation Seed Drawing ellipse(baseX, baseY - 10, 20, 14)

Draws the initial seed as a small brown ellipse at the soil surface

conditional Stem Growth Logic if (t > 0.1) { ... }

After 10% of animation, draws the stem growing upward and adds leaves at specific growth stages

conditional Left Leaf Appearance if (t > 0.3) ellipse(baseX - 25, baseY - 10 - stemH * 0.4, 30, 16)

Adds a left leaf when 30% of animation is complete, positioned at 40% of stem height

conditional Right Leaf Appearance if (t > 0.4) ellipse(baseX + 25, baseY - 10 - stemH * 0.6, 30, 16)

Adds a right leaf when 40% of animation is complete, positioned at 60% of stem height

for-loop Flower Petals Loop for (let i = 0; i < 6; i++) { let a = TWO_PI * i / 6; ellipse(fx + 20 * cos(a), fy + 20 * sin(a), 20, 28); }

Creates 6 pink petals arranged in a circle around the flower center using trigonometry

calculation Flower Center ellipse(fx, fy, 18, 18)

Draws the yellow center of the flower

conditional Stage Label Logic if (t > 0.1) stage = "Sprouting"; if (t > 0.3) stage = "Growing"; if (t > 0.6) stage = "Blooming"; if (t > 0.9) stage = "Full Bloom!"

Updates the stage text label based on animation progress

Line by Line:

background(180, 220, 255)
Fills the entire canvas with a light blue color (sky), clearing the previous frame
let soilH = 80
Stores the height of the soil rectangle in pixels
noStroke()
Disables outline drawing for the next shapes
fill(120, 70, 30)
Sets the fill color to brown for the soil
rect(0, height - soilH, width, soilH)
Draws a brown rectangle starting at the bottom of the canvas with width equal to canvas width and height of 80 pixels
let t = constrain((millis() - startTime) / 10000, 0, 1)
Calculates animation progress: (current time - start time) / 10000 milliseconds, then constrains it between 0 and 1. At t=0 the animation starts, at t=1 it's complete
let baseX = width / 2, baseY = height - soilH
Sets the base position of the plant at the center horizontally and at the soil surface vertically
fill(150, 90, 40)
Sets fill color to a darker brown for the seed
ellipse(baseX, baseY - 10, 20, 14)
Draws the seed as a small brown ellipse slightly above the soil surface
let stemMax = height - soilH - 120
Calculates the maximum stem height, leaving 120 pixels of space at the top for the flower
let stemH = stemMax * t
Calculates current stem height by multiplying maximum stem height by progress (t). As t goes from 0 to 1, stem grows from 0 to full height
if (t > 0.1) {
Only draw the stem and leaves after 10% of the animation has passed (after 1 second)
stroke(40, 150, 60)
Sets the stroke color to green for the stem
strokeWeight(6)
Sets the stem thickness to 6 pixels
line(baseX, baseY - 10, baseX, baseY - 10 - stemH)
Draws a vertical line from the seed upward, with height equal to current stem height
if (t > 0.3) ellipse(baseX - 25, baseY - 10 - stemH * 0.4, 30, 16)
After 30% of animation, draws a left leaf positioned at 40% of the stem's current height
if (t > 0.4) ellipse(baseX + 25, baseY - 10 - stemH * 0.6, 30, 16)
After 40% of animation, draws a right leaf positioned at 60% of the stem's current height
if (t > 0.6) {
Only draw the flower after 60% of the animation has passed (after 6 seconds)
let fx = baseX, fy = baseY - 10 - stemH
Sets the flower position at the top of the stem
fill(255, 80, 140)
Sets fill color to pink for the flower petals
for (let i = 0; i < 6; i++) {
Loops 6 times to create 6 petals arranged in a circle
let a = TWO_PI * i / 6
Calculates the angle for each petal: divides the full circle (TWO_PI radians) into 6 equal parts
ellipse(fx + 20 * cos(a), fy + 20 * sin(a), 20, 28)
Draws a petal at a position calculated using cos and sin, placing it 20 pixels away from the flower center at angle a
fill(255, 230, 140)
Sets fill color to yellow for the flower center
ellipse(fx, fy, 18, 18)
Draws a yellow circle at the center of the flower
let stage = "Seed"
Initializes the stage label to 'Seed'
if (t > 0.1) stage = "Sprouting"
Updates label to 'Sprouting' after 10% of animation
if (t > 0.3) stage = "Growing"
Updates label to 'Growing' after 30% of animation
if (t > 0.6) stage = "Blooming"
Updates label to 'Blooming' after 60% of animation
if (t > 0.9) stage = "Full Bloom!"
Updates label to 'Full Bloom!' after 90% of animation
fill(40, 80, 120)
Sets text color to dark blue
text(stage, width / 2, 20)
Displays the current stage label at the top center of the canvas

windowResized()

windowResized() is a special p5.js function that automatically gets called whenever the user resizes their browser window. By calling resizeCanvas() here, we ensure the sketch always fills the entire screen, maintaining responsiveness across different devices and window sizes.

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
}

Line by Line:

resizeCanvas(windowWidth, windowHeight)
Automatically resizes the canvas to match the current window dimensions whenever the browser window is resized

📦 Key Variables

startTime number

Stores the time in milliseconds when the sketch started. Used to calculate how much time has elapsed for animation timing.

let startTime; // declared globally, set in setup() with startTime = millis();
soilH number

Stores the height of the soil rectangle in pixels (80 pixels). Defines where the plant grows from.

let soilH = 80;
t number

Stores the animation progress value from 0 to 1. At t=0 animation starts, at t=1 it's complete. Used to trigger different plant growth stages.

let t = constrain((millis() - startTime) / 10000, 0, 1);
baseX number

Stores the horizontal center position of the plant (middle of the canvas). Used as the x-coordinate for the seed, stem, and flower.

let baseX = width / 2;
baseY number

Stores the vertical position where the plant emerges from the soil (at the soil surface). Used as the starting y-coordinate for the stem.

let baseY = height - soilH;
stemMax number

Stores the maximum height the stem can grow to, calculated as canvas height minus soil height minus 120 pixels (space for flower).

let stemMax = height - soilH - 120;
stemH number

Stores the current stem height at the current animation frame. Grows from 0 to stemMax as animation progresses.

let stemH = stemMax * t;
fx number

Stores the horizontal position of the flower (same as baseX, at the center of the canvas).

let fx = baseX;
fy number

Stores the vertical position of the flower (at the top of the stem).

let fy = baseY - 10 - stemH;
a number

Stores the angle for each flower petal in the petal-drawing loop. Divides the circle into 6 equal angles.

let a = TWO_PI * i / 6;
stage string

Stores the current growth stage label ('Seed', 'Sprouting', 'Growing', 'Blooming', or 'Full Bloom!') to display at the top of the canvas.

let stage = "Seed";

🧪 Try This!

Experiment with the code by making these changes:

  1. Change the animation duration from 10000 milliseconds to 5000 to make the plant grow twice as fast. Find the line 'let t = constrain((millis() - startTime) / 10000, 0, 1)' and change 10000 to 5000.
  2. Modify the flower petal color by changing the fill color from fill(255, 80, 140) to fill(255, 0, 255) to make purple petals instead of pink.
  3. Increase the number of petals from 6 to 8 by changing 'for (let i = 0; i < 6; i++)' to 'for (let i = 0; i < 8; i++)' and changing '/ 6' to '/ 8' in the angle calculation.
  4. Change when leaves appear by modifying the t threshold values: change 'if (t > 0.3)' to 'if (t > 0.2)' to make the left leaf appear earlier in the animation.
  5. Make the stem thicker by changing strokeWeight(6) to strokeWeight(10) to create a more robust plant stem.
  6. Change the soil color by modifying fill(120, 70, 30) to fill(139, 69, 19) for a different shade of brown.
Open in Editor & Experiment →

🔧 Potential Improvements

Here are some ways this code could be enhanced:

BUG draw() - flower petal positioning

The flower petals are positioned using cos(a) and sin(a) which expect radians, but the petal size (20, 28) is fixed and doesn't scale with stem height or canvas size, making it look disconnected on very large or small screens.

💡 Scale the petal distance based on canvas size: use 'ellipse(fx + stemMax * 0.15 * cos(a), fy + stemMax * 0.15 * sin(a), 20, 28)' to make petals proportional to plant size.

PERFORMANCE draw() - stage label updates

The stage label is recalculated every frame even though it only changes at specific time thresholds, causing unnecessary string comparisons.

💡 Use a single if-else chain instead of multiple if statements: 'if (t <= 0.1) stage = "Seed"; else if (t <= 0.3) stage = "Sprouting"; else if (t <= 0.6) stage = "Growing"; else if (t <= 0.9) stage = "Blooming"; else stage = "Full Bloom!";'

STYLE setup() and draw()

Magic numbers like 0.1, 0.3, 0.4, 0.6, 0.9, 120, 80 are scattered throughout the code without explanation, making it hard to adjust timing or proportions.

💡 Define constants at the top of the sketch: 'const SEED_STAGE = 0.1; const SPROUT_STAGE = 0.3; const LEAF1_STAGE = 0.3; const LEAF2_STAGE = 0.4; const FLOWER_STAGE = 0.6; const FULL_BLOOM = 0.9; const SOIL_HEIGHT = 80; const FLOWER_SPACE = 120;' then use these names instead of numbers.

FEATURE draw() - animation loop

The animation completes at t=1 and then stops, with no way to restart or loop the growth cycle.

💡 Add a restart button or automatically loop the animation by modifying the time calculation: 'let elapsed = (millis() - startTime) % 10000; let t = constrain(elapsed / 10000, 0, 1);' This uses the modulo operator (%) to restart the animation every 10 seconds.

STYLE draw() - leaf positioning

Leaf positions are hardcoded with specific offset values (-25, +25) and stem height multipliers (0.4, 0.6), making it unclear why these values were chosen.

💡 Add comments explaining the positioning: '// Left leaf at 40% of stem height, offset 25 pixels left' and '// Right leaf at 60% of stem height, offset 25 pixels right' to make the intent clear.

Preview

AI Growing Plant - Seed to Flower Journey - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of AI Growing Plant - Seed to Flower Journey - Code flow showing setup, draw, windowresized
Code Flow Diagram