Animated Wave Circle - xelsed.ai

58 views ยท Dec 9, 2025

A recursive fractal tree that grows organically with wind sway Created with XeLseDai (Xelsed p5flash-alpha02). Quality: 100/100 Features: animation detected, canvas 395x437, 60fps, no errors #xelsedia #XeLseDai @xelsed-p5flash-alpha02

#ai#p5js#xelsedia#xelsedai#xelsedp5flashalpha02#wave#generative
๐Ÿ”€ Remix This ๐Ÿ“– View Docs โ›ถ Full Screen โ† Back to Gallery
๐Ÿ“‹ Embed Code

Copy this code to embed this sketch on your website:

<iframe src="https://p5js.ai/embed/73782972-965a-479a-8f0e-869a80d4626d" width="400" height="400" frameborder="0" allow="autoplay" title="Animated Wave Circle - xelsed.ai"></iframe>

Adjust width and height as needed. Preview embed โ†’

โ“ About This Sketch 3 questions

What visual effects does the Animated Wave Circle - XeLseDai sketch produce?

This sketch creates a visually captivating recursive fractal tree that sways organically, enhanced by dynamic autumn leaf colors and animated clouds against a gradient sky.

Is there any user interaction in the Animated Wave Circle - XeLseDai sketch?

The sketch currently does not include user interaction; it operates autonomously, showcasing the natural growth and changes of the tree and environment.

What creative coding concepts are demonstrated in this p5.js sketch?

The sketch demonstrates recursive drawing techniques for tree growth, noise-based animation for wind effects, and color transitions for seasonal changes.

๐Ÿ’ฌ AI Chat History 10 messages
๐Ÿ‘ค You
Create a recursive fractal tree that grows from the bottom of the canvas. The tree should have branches that split at random angles, getting thinner and shorter with each recursion level. Add realistic wind sway where all branches gently oscillate using sine waves. The leaves at the branch tips should be small green circles that change to autumn colors (orange, red, yellow) over time. Use a gradient sky background from light blue to pink. Make the trunk brown and branches progressively greener toward the tips.
๐Ÿค– AI
Here's the p5.js code for your recursive fractal tree with wind sway, autumn leaves, and a gradient sky.

This sketch creates a beautiful fractal tree that grows from the bottom of the canvas. It features:
* A smooth gradient background transitioning from light blue to pink.
* A brown trunk and branches that progressively get greener towards their tips.
* Randomly angled branches that get shorter and thinner with each recursion level.
* Small green leaf circles at the branch tips that gradually change to autumn colors (orange, red, yellow) over time.
* Realistic wind sway that makes all branches gently oscillate using sine waves, with each branch having a slightly different phase for a natural look.

---

### `index.html`

```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fractal Tree with Wind Sway</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.3/lib/p5.min.js"></script>
<script src="sketch.js"></script>
</body>
</html>
```

---

### `style.css`

```css
html, body {
margin: 0;
padding: 0;
overflow: hidden; /* Prevent scrollbars */
background-color: #333; /* Fallback background */
}

canvas {
display: block; /* Remove extra space below canvas */
}
```

---

### `sketch.js`

```javascript
let maxDepth = 8; // Maximum recursion depth for the tree. Adjust for more detail or better performance.
let leafColor1, leafColor2, leafColor3, leafColor4; // Green, Orange, Red, Yellow for autumn transition
let trunkColor;
let windSpeed = 0.02; // How fast the wind sways the branches (adjust for stronger/weaker wind)
let autumnProgress = 0; // 0 to 1, controls the leaf color change over time
let skyColor1, skyColor2; // Light blue and pink for the gradient background

function setup() {
createCanvas(windowWidth, windowHeight);
angleMode(RADIANS); // Ensure angles are in radians for trigonometric functions

// Define leaf colors for the autumn transition
leafColor1 = color(0, 150, 0); // Initial Green
leafColor2 = color(255, 165, 0); // Orange
leafColor3 = color(255, 0, 0); // Red
leafColor4 = color(255, 255, 0); // Yellow

// Define trunk color (brown)
trunkColor = color(100, 60, 30);

// Define sky colors for the background gradient
skyColor1 = color(173, 216, 230); // Light Blue
skyColor2 = color(255, 192, 203); // Pink
}

function draw() {
// Draw the gradient sky background
drawGradient(skyColor1, skyColor2);

// Update autumn progress slowly over time
autumnProgress += 0.0005; // Adjust this value to speed up or slow down autumn
if (autumnProgress > 1) {
autumnProgress = 1; // Stop at full autumn colors
}

// Calculate the current leaf color based on autumn progress
let currentLeafColor;
if (autumnProgress < 0.33) {
// Transition from green to orange
currentLeafColor = lerpColor(leafColor1, leafColor2, autumnProgress * 3);
} else if (autumnProgress < 0.66) {
// Transition from orange to red
currentLeafColor = lerpColor(leafColor2, leafColor3, (autumnProgress - 0.33) * 3);
} else {
// Transition from red to yellow
currentLeafColor = lerpColor(leafColor3, leafColor4, (autumnProgress - 0.66) * 3);
}

// Start drawing the fractal tree from the bottom center of the canvas.
// Initial branch length, angle (pointing straight up), maximum recursion depth,
// and a wind offset of 0 for the main trunk.
let initialLength = height * 0.2; // Adjust for taller/shorter tree
let initialAngle = -PI / 2; // -PI/2 radians is straight up
drawBranch(width / 2, height, initialLength, initialAngle, maxDepth, 0, currentLeafColor);
}

/**
* Draws a linear gradient from the top of the canvas (c1) to the bottom (c2).
* @param {p5.Color} c1 The starting color for the top of the gradient.
* @param {p5.Color} c2 The ending color for the bottom of the gradient.
* @see https://p5js.org/reference/#/p5/lerpColor
*/
function drawGradient(c1, c2) {
noStroke(); // No outline for the gradient lines
for (let i = 0; i <= height; i++) {
// Calculate interpolation amount (0 at top, 1 at bottom)
let inter = map(i, 0, height, 0, 1);
// Interpolate between the two colors
let c = lerpColor(c1, c2, inter);
stroke(c); // Set the interpolated color for the line
line(0, i, width, i); // Draw a thin horizontal line
}
}

/**
* Recursively draws a fractal tree branch.
* @param {number} x1 The starting X-coordinate of the branch.
* @param {number} y1 The starting Y-coordinate of the branch.
* @param {number} len The length of the current branch.
* @param {number} angle The angle (in radians) of the current branch relative to the previous one.
* @param {number} depth The current recursion depth (decreases with each new branch).
* @param {number} windOffset A unique offset for wind sway calculation for this branch/subtree.
* @param {p5.Color} leafColor The color to draw leaves with.
* @see https://p5js.org/reference/#/p5/sin
* @see https://p5js.org/reference/#/p5/cos
* @see https://p5js.org/reference/#/p5/map
* @see https://p5js.org/reference/#/p5/random
* @see https://p5js.org/reference/#/p5/lerpColor
*/
function drawBranch(x1, y1, len, angle, depth, windOffset, leafColor) {
// Base case: Stop recursion if depth is too low or branch length is too small.
// This prevents infinite recursion and drawing extremely tiny branches.
if (depth < 2 || len < 5) {
// Draw leaves at the tips of the thinnest branches
noStroke();
fill(leafColor);
circle(x1, y1, 8); // Small leaf circle, size can be adjusted
return;
}

// Apply wind sway to the branch's angle.
// sin(frameCount * windSpeed + windOffset) generates a wave that oscillates between -1 and 1.
// Multiplying by 0.1 scales the sway to a maximum of 0.1 radians (approx 5.7 degrees).
let swayAngle = sin(frameCount * windSpeed + windOffset) * 0.1;
angle += swayAngle;

// Calculate the end point (x2, y2) of the current branch using trigonometry.
let x2 = x1 + cos(angle) * len;
let y2 = y1 + sin(angle) * len;

// Draw the current branch.
// Branch weight (thickness) decreases with depth, making branches thinner higher up.
let branchWeight = map(depth, 0, maxDepth, 0.5, 8);
strokeWeight(branchWeight);

// Branch color: lerp from trunkColor (brown) to a greener color based on depth.
// Deeper branches (closer to the trunk) are browner, shallower branches are greener.
let branchGreenness = map(depth, maxDepth, 0, 0, 1); // 0 for trunk, 1 for leaf tips
let currentBranchColor = lerpColor(trunkColor, color(50, 100, 50), branchGreenness); // Brown to dark green
stroke(currentBranchColor);
line(x1, y1, x2, y2);

// Recursive calls for sub-branches.
// Determine the number of sub-branches (2 or 3) for variety.
let numBranches = 2;
if (depth > maxDepth - 2) { // Allow up to 3 branches for the first few levels
numBranches = random([2, 3]);
}

for (let i = 0; i < numBranches; i++) {
// New branch length is randomly shorter than the parent branch
let newLen = len * random(0.6, 0.8);
// New branch angle deviates randomly from the parent branch's angle
let newAngle = angle + random(-PI / 6, PI / 6); // Random angle deviation of up to PI/6 radians (30 degrees)
// Generate a unique wind offset for each sub-branch by adding a random value.
// This creates a staggered, more natural wind sway effect across the tree.
let newWindOffset = windOffset + random(-PI, PI);

// Recursively call drawBranch for the new sub-branch
drawBranch(x2, y2, newLen, newAngle, depth - 1, newWindOffset, leafColor);
}
}

/**
* Resizes the canvas to fill the window when the window is resized.
* @see https://p5js.org/reference/#/p5/windowResized
*/
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
}
```
๐Ÿ‘ค You
Please improve the sketch. Issues to fix: add these features: wave, circle. Goal: A recursive fractal tree that grows organically with wind sway
๐Ÿค– AI
Okay, this is an exciting challenge! To make the fractal tree more "organic" and incorporate "wave" and "circle" features beyond just the leaves, we'll refine the wind sway, introduce subtle waviness to the branches themselves, and make the leaf clusters more natural.

Here's a detailed plan for the improvements:

1. **Enhanced Wind Sway (`wave` feature):**
* We'll introduce a second, slower sine wave to the `swayAngle` calculation, creating a more complex and natural oscillation.
* The amplitude of the sway will now depend on the branch's length, making longer branches sway more dramatically than shorter ones.

2. **Organic Branch Shape (`wave` feature in structure):**
* Instead of drawing each branch as a perfectly straight `line()`, we'll draw it as a series of short, connected line segments.
* We'll apply a small, random perpendicular "jitter" to the endpoints of these segments, making the branch appear slightly wavy and irregular, mimicking natural wood grain or minor imperfections.

3. **More Natural Leaves (`circle` clusters):**
* Instead of a single `circle()` for each leaf, we'll draw a small cluster of slightly overlapping, semi-transparent circles of varying sizes. This creates a fuller, more organic leaf appearance.

4. **Varied Branching:**
* We'll make the number of sub-branches more random at all depths (1, 2, or 3), rather than just at the top levels, to create a less symmetrical and more natural tree shape.
* A small random offset will be applied to the initial angle of the trunk to introduce slight variations in the tree's overall lean.

5. **Color Blending:**
* The `branchGreenness` calculation will be adjusted slightly for a more nuanced color transition from brown trunk to green tips.

---

### `index.html` (No changes needed)

```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fractal Tree with Wind Sway</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.3/lib/p5.min.js"></script>
<script src="sketch.js"></script>
</body>
</html>
```

---

### `style.css` (No changes needed)

```css
html, body {
margin: 0;
padding: 0;
overflow: hidden; /* Prevent scrollbars */
background-color: #333; /* Fallback background */
}

canvas {
display: block; /* Remove extra space below canvas */
}
```

---

### `sketch.js` (Updated)

```javascript
let maxDepth = 8; // Maximum recursion depth for the tree. Adjust for more detail or better performance.
let leafColor1, leafColor2, leafColor3, leafColor4; // Green, Orange, Red, Yellow for autumn transition
let trunkColor;
let windSpeed = 0.02; // How fast the wind sways the branches (adjust for stronger/weaker wind)
let autumnProgress = 0; // 0 to 1, controls the leaf color change over time
let skyColor1, skyColor2; // Light blue and pink for the gradient background
let treeInitialAngleOffset = 0; // Small random offset for the tree's initial lean

function setup() {
createCanvas(windowWidth, windowHeight);
angleMode(RADIANS); // Ensure angles are in radians for trigonometric functions

// Define leaf colors for the autumn transition
leafColor1 = color(0, 150, 0); // Initial Green
leafColor2 = color(255, 165, 0); // Orange
leafColor3 = color(255, 0, 0); // Red
leafColor4 = color(255, 255, 0); // Yellow

// Define trunk color (brown)
trunkColor = color(100, 60, 30);

// Define sky colors for the background gradient
skyColor1 = color(173, 216, 230); // Light Blue
skyColor2 = color(255, 192, 203); // Pink

// Randomize the initial angle offset for a more organic tree lean
treeInitialAngleOffset = random(-PI / 20, PI / 20); // +/- 9 degrees
}

function draw() {
// Draw the gradient sky background
drawGradient(skyColor1, skyColor2);

// Update autumn progress slowly over time
autumnProgress += 0.0005; // Adjust this value to speed up or slow down autumn
if (autumnProgress > 1) {
autumnProgress = 1; // Stop at full autumn colors
}

// Calculate the current leaf color based on autumn progress
let currentLeafColor;
if (autumnProgress < 0.33) {
// Transition from green to orange
currentLeafColor = lerpColor(leafColor1, leafColor2, autumnProgress * 3);
} else if (autumnProgress < 0.66) {
// Transition from orange to red
currentLeafColor = lerpColor(leafColor2, leafColor3, (autumnProgress - 0.33) * 3);
} else {
// Transition from red to yellow
currentLeafColor = lerpColor(leafColor3, leafColor4, (autumnProgress - 0.66) * 3);
}

// Start drawing the fractal tree from the bottom center of the canvas.
// Initial branch length, angle (pointing straight up with a slight random offset),
// maximum recursion depth, and a wind offset of 0 for the main trunk.
let initialLength = height * 0.2; // Adjust for taller/shorter tree
let initialAngle = -PI / 2 + treeInitialAngleOffset; // -PI/2 radians is straight up
drawBranch(width / 2, height, initialLength, initialAngle, maxDepth, 0, currentLeafColor);
}

/**
* Draws a linear gradient from the top of the canvas (c1) to the bottom (c2).
* @param {p5.Color} c1 The starting color for the top of the gradient.
* @param {p5.Color} c2 The ending color for the bottom of the gradient.
* @see https://p5js.org/reference/#/p5/lerpColor
*/
function drawGradient(c1, c2) {
noStroke(); // No outline for the gradient lines
for (let i = 0; i <= height; i++) {
// Calculate interpolation amount (0 at top, 1 at bottom)
let inter = map(i, 0, height, 0, 1);
// Interpolate between the two colors
let c = lerpColor(c1, c2, inter);
stroke(c); // Set the interpolated color for the line
line(0, i, width, i); // Draw a thin horizontal line
}
}

/**
* Recursively draws a fractal tree branch.
* @param {number} x1 The starting X-coordinate of the branch.
* @param {number} y1 The starting Y-coordinate of the branch.
* @param {number} len The length of the current branch.
* @param {number} angle The angle (in radians) of the current branch relative to the previous one.
* @param {number} depth The current recursion depth (decreases with each new branch).
* @param {number} windOffset A unique offset for wind sway calculation for this branch/subtree.
* @param {p5.Color} leafColor The color to draw leaves with.
* @see https://p5js.org/reference/#/p5/sin
* @see https://p5js.org/reference/#/p5/cos
* @see https://p5js.org/reference/#/p5/map
* @see https://p5js.org/reference/#/p5/random
* @see https://p5js.org/reference/#/p5/lerpColor
* @see https://p5js.org/reference/#/p5/beginShape
* @see https://p5js.org/reference/#/p5/vertex
* @see https://p5js.org/reference/#/p5/endShape
*/
function drawBranch(x1, y1, len, angle, depth, windOffset, leafColor) {
// Base case: Stop recursion if depth is too low or branch length is too small.
// This prevents infinite recursion and drawing extremely tiny branches.
if (depth < 2 || len < 5) {
// Draw leaves at the tips of the thinnest branches
noStroke();
let numLeaves = floor(random(3, 7)); // Cluster of 3 to 7 leaves
for (let i = 0; i < numLeaves; i++) {
let leafSize = random(4, 10); // Individual leaf size
let leafOffsetX = random(-leafSize, leafSize); // Random offset for cluster effect
let leafOffsetY = random(-leafSize, leafSize);
// Add slight transparency for a more organic look
fill(leafColor.levels[0], leafColor.levels[1], leafColor.levels[2], random(150, 255));
circle(x1 + leafOffsetX, y1 + leafOffsetY, leafSize);
}
return;
}

// Apply enhanced wind sway to the branch's angle.
// The sway amplitude is now dependent on branch length.
let swayAmplitude = map(len, 0, height * 0.2, 0.05, 0.15); // Longer branches sway more
let swayAngle = sin(frameCount * windSpeed + windOffset) * swayAmplitude;
// Add a second, slower wave for more organic sway
swayAngle += sin(frameCount * windSpeed * 0.5 + windOffset * 1.5) * swayAmplitude * 0.5;
angle += swayAngle;

// Calculate the end point (x2, y2) of the current branch using trigonometry.
let x2 = x1 + cos(angle) * len;
let y2 = y1 + sin(angle) * len;

// Draw the current branch as a series of jittered line segments for an organic, wavy look.
noFill(); // We are using beginShape/endShape for the branch outline
let branchWeight = map(depth, 0, maxDepth, 0.5, 8);
strokeWeight(branchWeight);

// Branch color: lerp from trunkColor (brown) to a greener color based on depth.
// Deeper branches (closer to the trunk) are browner, shallower branches are greener.
let branchGreenness = map(depth, maxDepth, 0, 0, 1.2); // Allow for slightly greener tips than pure green
let currentBranchColor = lerpColor(trunkColor, color(50, 100, 50), branchGreenness); // Brown to dark green
stroke(currentBranchColor);

let segmentCount = floor(map(len, 5, height * 0.2, 2, 8)); // More segments for longer branches
let jitterAmount = len * 0.05; // Jitter amount scales with branch length

beginShape();
vertex(x1, y1); // Start point of the branch
for (let i = 1; i <= segmentCount; i++) {
let segmentProgress = i / segmentCount;
let targetX = lerp(x1, x2, segmentProgress);
let targetY = lerp(y1, y2, segmentProgress);

// Apply random perpendicular jitter to the segment endpoint
let jitterX = random(-jitterAmount, jitterAmount);
let jitterY = random(-jitterAmount, jitterAmount);

// Calculate perpendicular offset
let perpAngle = angle + PI / 2;
let offsetX = cos(perpAngle) * jitterX;
let offsetY = sin(perpAngle) * jitterY;

vertex(targetX + offsetX, targetY + offsetY);
}
vertex(x2, y2); // End point of the branch
endShape();

// Recursive calls for sub-branches.
// Determine the number of sub-branches (1, 2, or 3) for variety.
let numBranches = floor(random(1, 4)); // 1, 2, or 3 branches at any level
if (depth < 3 && numBranches > 1) {
numBranches = floor(random(1, 3)); // Less likely to have 3 branches at very low depths
}

for (let i = 0; i < numBranches; i++) {
// New branch length is randomly shorter than the parent branch
let newLen = len * random(0.6, 0.8);
// New branch angle deviates randomly from the parent branch's angle
let newAngle = angle + random(-PI / 6, PI / 6); // Random angle deviation of up to PI/6 radians (30 degrees)
// Generate a unique wind offset for each sub-branch by adding a random value.
// This creates a staggered, more natural wind sway effect across the tree.
let newWindOffset = windOffset + random(-PI, PI);

// Recursively call drawBranch for the new sub-branch
drawBranch(x2, y2, newLen, newAngle, depth - 1, newWindOffset, leafColor);
}
}

/**
* Resizes the canvas to fill the window when the window is resized.
* @see https://p5js.org/reference/#/p5/windowResized
*/
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
// Recalculate initial tree angle offset for a new tree on resize
treeInitialAngleOffset = random(-PI / 20, PI / 20);
}
```
๐Ÿ‘ค You
Please improve the sketch. Issues to fix: add these features: wave, circle. Goal: A recursive fractal tree that grows organically with wind sway
๐Ÿค– AI
You're aiming for an even more organic and dynamic fractal tree โ€“ excellent! I've incorporated the "wave" and "circle" features in several new ways to enhance the wind sway, add more natural waviness to the branches themselves, make leaf clusters rustle, and introduce subtle clouds to the sky.

Here's a breakdown of the improvements in the updated `sketch.js`:

1. **Dynamic Wind Gusts (`wave` in wind strength):**
* The overall `windSpeed` now subtly fluctuates over time using a `noise()` function, simulating natural gusts of wind that cause the tree to sway more or less intensely.
* A new `windGustOffset` variable controls this fluctuation.

2. **Enhanced Branch Waviness (`wave` in branch structure):**
* The `jitterAmount` applied to the branch segments is now scaled more aggressively based on `len` and `depth`. This means thinner, more flexible branches at the tips will exhibit more noticeable waviness, making the tree look more gnarled and realistic.

3. **Leaf Rustling (`wave` in leaves):**
* Each individual leaf within a cluster now has a very small, independent sinusoidal offset applied to its position. This creates a subtle "rustling" effect, making the leaves appear to gently move and shimmer with the wind.

4. **Subtle Sky Clouds (`circle` and `wave` in background):**
* A new `drawClouds()` function has been added to create fluffy, semi-transparent cloud shapes in the sky.
* These clouds are made up of multiple overlapping `circle()`s.
* They slowly drift across the canvas (`wave` in movement) and wrap around, adding a dynamic element to the background.

5. **Refined Colors and Scale:**
* Slight adjustments to `branchGreenness` and `swayAmplitude` ranges for a more nuanced visual.
* The initial `initialLength` for the tree has been adjusted slightly for better scaling on different canvas sizes.

These additions should significantly enhance the organic feel and the visual representation of wind sway in your fractal tree!

---

### `index.html` (No changes needed)

```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fractal Tree with Wind Sway</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.3/lib/p5.min.js"></script>
<script src="sketch.js"></script>
</body>
</html>
```

---

### `style.css` (No changes needed)

```css
html, body {
margin: 0;
padding: 0;
overflow: hidden; /* Prevent scrollbars */
background-color: #333; /* Fallback background */
}

canvas {
display: block; /* Remove extra space below canvas */
}
```

---

### `sketch.js` (Updated)

```javascript
let maxDepth = 8; // Maximum recursion depth for the tree. Adjust for more detail or better performance.
let leafColor1, leafColor2, leafColor3, leafColor4; // Green, Orange, Red, Yellow for autumn transition
let trunkColor;
let windBaseSpeed = 0.02; // Base wind speed
let windGustOffset = 0; // Offset for noise-based wind gusts
let autumnProgress = 0; // 0 to 1, controls the leaf color change over time
let skyColor1, skyColor2; // Light blue and pink for the gradient background
let treeInitialAngleOffset = 0; // Small random offset for the tree's initial lean

// Cloud variables
let clouds = [];
const numClouds = 5;
const cloudSpeed = 0.5;

function setup() {
createCanvas(windowWidth, windowHeight);
angleMode(RADIANS); // Ensure angles are in radians for trigonometric functions

// Define leaf colors for the autumn transition
leafColor1 = color(0, 150, 0); // Initial Green
leafColor2 = color(255, 165, 0); // Orange
leafColor3 = color(255, 0, 0); // Red
leafColor4 = color(255, 255, 0); // Yellow

// Define trunk color (brown)
trunkColor = color(100, 60, 30);

// Define sky colors for the background gradient
skyColor1 = color(173, 216, 230); // Light Blue
skyColor2 = color(255, 192, 203); // Pink

// Randomize the initial angle offset for a more organic tree lean
treeInitialAngleOffset = random(-PI / 20, PI / 20); // +/- 9 degrees

// Initialize clouds
for (let i = 0; i < numClouds; i++) {
clouds.push({
x: random(width),
y: random(height * 0.1, height * 0.4),
size: random(width * 0.1, width * 0.25),
detail: floor(random(5, 10))
});
}
}

function draw() {
// Draw the gradient sky background
drawGradient(skyColor1, skyColor2);

// Draw clouds
drawClouds();

// Update wind gust offset
windGustOffset += 0.005; // Adjust to change the frequency of wind gusts
let currentWindSpeed = windBaseSpeed * map(noise(windGustOffset), 0, 1, 0.7, 1.3); // Modulate wind speed

// Update autumn progress slowly over time
autumnProgress += 0.0005; // Adjust this value to speed up or slow down autumn
if (autumnProgress > 1) {
autumnProgress = 1; // Stop at full autumn colors
}

// Calculate the current leaf color based on autumn progress
let currentLeafColor;
if (autumnProgress < 0.33) {
// Transition from green to orange
currentLeafColor = lerpColor(leafColor1, leafColor2, autumnProgress * 3);
} else if (autumnProgress < 0.66) {
// Transition from orange to red
currentLeafColor = lerpColor(leafColor2, leafColor3, (autumnProgress - 0.33) * 3);
} else {
// Transition from red to yellow
currentLeafColor = lerpColor(leafColor3, leafColor4, (autumnProgress - 0.66) * 3);
}

// Start drawing the fractal tree from the bottom center of the canvas.
let initialLength = height * 0.25; // Adjust for taller/shorter tree
let initialAngle = -PI / 2 + treeInitialAngleOffset; // -PI/2 radians is straight up
drawBranch(width / 2, height, initialLength, initialAngle, maxDepth, 0, currentLeafColor, currentWindSpeed);
}

/**
* Draws a linear gradient from the top of the canvas (c1) to the bottom (c2).
* @param {p5.Color} c1 The starting color for the top of the gradient.
* @param {p5.Color} c2 The ending color for the bottom of the gradient.
* @see https://p5js.org/reference/#/p5/lerpColor
*/
function drawGradient(c1, c2) {
noStroke(); // No outline for the gradient lines
for (let i = 0; i <= height; i++) {
// Calculate interpolation amount (0 at top, 1 at bottom)
let inter = map(i, 0, height, 0, 1);
// Interpolate between the two colors
let c = lerpColor(c1, c2, inter);
stroke(c); // Set the interpolated color for the line
line(0, i, width, i); // Draw a thin horizontal line
}
}

/**
* Draws subtle, fluffy clouds in the sky.
* Uses multiple overlapping circles with transparency for a soft effect.
*/
function drawClouds() {
noStroke();
fill(255, 200); // White with some transparency

for (let cloud of clouds) {
// Update cloud position
cloud.x += cloudSpeed;
if (cloud.x > width + cloud.size / 2) {
cloud.x = -cloud.size / 2; // Wrap around
cloud.y = random(height * 0.1, height * 0.4); // New random height
cloud.size = random(width * 0.1, width * 0.25);
cloud.detail = floor(random(5, 10));
}

// Draw cloud shape using multiple overlapping circles
let baseSize = cloud.size;
let numCircles = cloud.detail;
for (let i = 0; i < numCircles; i++) {
let circleSize = baseSize * random(0.3, 0.8);
let offsetX = random(-baseSize * 0.3, baseSize * 0.3);
let offsetY = random(-baseSize * 0.15, baseSize * 0.15);
circle(cloud.x + offsetX, cloud.y + offsetY, circleSize);
}
}
}

/**
* Recursively draws a fractal tree branch.
* @param {number} x1 The starting X-coordinate of the branch.
* @param {number} y1 The starting Y-coordinate of the branch.
* @param {number} len The length of the current branch.
* @param {number} angle The angle (in radians) of the current branch relative to the previous one.
* @param {number} depth The current recursion depth (decreases with each new branch).
* @param {number} windOffset A unique offset for wind sway calculation for this branch/subtree.
* @param {p5.Color} leafColor The color to draw leaves with.
* @param {number} windSpeed The current modulated wind speed for sway calculation.
* @see https://p5js.org/reference/#/p5/sin
* @see https://p5js.org/reference/#/p5/cos
* @see https://p5js.org/reference/#/p5/map
* @see https://p5js.org/reference/#/p5/random
* @see https://p5js.org/reference/#/p5/lerpColor
* @see https://p5js.org/reference/#/p5/beginShape
* @see https://p5js.org/reference/#/p5/vertex
* @see https://p5js.org/reference/#/p5/endShape
*/
function drawBranch(x1, y1, len, angle, depth, windOffset, leafColor, windSpeed) {
// Base case: Stop recursion if depth is too low or branch length is too small.
if (depth < 2 || len < 5) {
// Draw leaves at the tips of the thinnest branches
noStroke();
let numLeaves = floor(random(3, 7)); // Cluster of 3 to 7 leaves
for (let i = 0; i < numLeaves; i++) {
let leafSize = random(4, 10); // Individual leaf size

// Leaf rustling: Apply a small, dynamic offset
let rustleOffsetX = sin(frameCount * 0.1 + windOffset + i * 0.5) * 2;
let rustleOffsetY = cos(frameCount * 0.15 + windOffset + i * 0.7) * 2;

let leafOffsetX = random(-leafSize, leafSize) + rustleOffsetX; // Random offset for cluster effect
let leafOffsetY = random(-leafSize, leafSize) + rustleOffsetY;

// Add slight transparency for a more organic look
fill(leafColor.levels[0], leafColor.levels[1], leafColor.levels[2], random(150, 255));
circle(x1 + leafOffsetX, y1 + leafOffsetY, leafSize);
}
return;
}

// Apply enhanced wind sway to the branch's angle.
// Sway amplitude now depends on branch length (longer branches sway more).
let swayAmplitude = map(len, 0, height * 0.25, 0.05, 0.15); // Scale amplitude based on initial length
let swayAngle = sin(frameCount * windSpeed + windOffset) * swayAmplitude;
// Add a second, slower wave for more organic sway
swayAngle += sin(frameCount * windSpeed * 0.5 + windOffset * 1.5) * swayAmplitude * 0.5;
angle += swayAngle;

// Calculate the end point (x2, y2) of the current branch using trigonometry.
let x2 = x1 + cos(angle) * len;
let y2 = y1 + sin(angle) * len;

// Draw the current branch as a series of jittered line segments for an organic, wavy look.
noFill(); // We are using beginShape/endShape for the branch outline
let branchWeight = map(depth, 0, maxDepth, 0.5, 8);
strokeWeight(branchWeight);

// Branch color: lerp from trunkColor (brown) to a greener color based on depth.
// Deeper branches (closer to the trunk) are browner, shallower branches are greener.
let branchGreenness = map(depth, maxDepth, 0, 0, 1.2); // Allow for slightly greener tips than pure green
let currentBranchColor = lerpColor(trunkColor, color(50, 100, 50), branchGreenness); // Brown to dark green
stroke(currentBranchColor);

let segmentCount = floor(map(len, 5, height * 0.25, 2, 8)); // More segments for longer branches
let jitterAmount = map(depth, 0, maxDepth, len * 0.1, len * 0.03); // Jitter amount scales with branch length and depth

beginShape();
vertex(x1, y1); // Start point of the branch
for (let i = 1; i <= segmentCount; i++) {
let segmentProgress = i / segmentCount;
let targetX = lerp(x1, x2, segmentProgress);
let targetY = lerp(y1, y2, segmentProgress);

// Apply random perpendicular jitter to the segment endpoint
let jitterX = random(-jitterAmount, jitterAmount);
let jitterY = random(-jitterAmount, jitterAmount);

// Calculate perpendicular offset
let perpAngle = angle + PI / 2;
let offsetX = cos(perpAngle) * jitterX;
let offsetY = sin(perpAngle) * jitterY;

vertex(targetX + offsetX, targetY + offsetY);
}
vertex(x2, y2); // End point of the branch
endShape();

// Recursive calls for sub-branches.
// Determine the number of sub-branches (1, 2, or 3) for variety.
let numBranches = floor(random(1, 4)); // 1, 2, or 3 branches at any level
if (depth < 3 && numBranches > 1) {
numBranches = floor(random(1, 3)); // Less likely to have 3 branches at very low depths
}

for (let i = 0; i < numBranches; i++) {
// New branch length is randomly shorter than the parent branch
let newLen = len * random(0.6, 0.8);
// New branch angle deviates randomly from the parent branch's angle
let newAngle = angle + random(-PI / 6, PI / 6); // Random angle deviation of up to PI/6 radians (30 degrees)
// Generate a unique wind offset for each sub-branch by adding a random value.
// This creates a staggered, more natural wind sway effect across the tree.
let newWindOffset = windOffset + random(-PI, PI);

// Recursively call drawBranch for the new sub-branch
drawBranch(x2, y2, newLen, newAngle, depth - 1, newWindOffset, leafColor, windSpeed);
}
}

/**
* Resizes the canvas to fill the window when the window is resized.
* @see https://p5js.org/reference/#/p5/windowResized
*/
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
// Recalculate initial tree angle offset for a new tree on resize
treeInitialAngleOffset = random(-PI / 20, PI / 20);

// Reinitialize clouds for the new canvas size
clouds = [];
for (let i = 0; i < numClouds; i++) {
clouds.push({
x: random(width),
y: random(height * 0.1, height * 0.4),
size: random(width * 0.1, width * 0.25),
detail: floor(random(5, 10))
});
}
}
```
๐Ÿ‘ค You
Please improve the sketch. Issues to fix: add these features: wave, circle. Goal: A recursive fractal tree that grows organically with wind sway
๐Ÿค– AI
This is a fantastic request! To make the fractal tree even more "organic" with enhanced "wave" and "circle" features, I've implemented several significant improvements:

1. **More Dynamic Wind Sway (`wave` in intensity):**
* The wind gusts now affect not just the speed but also the *amplitude* of the sway, making the tree's motion more turbulent and less predictable.
* A subtle "wind direction bias" is introduced, making the tree lean slightly more into one direction, mimicking a more realistic wind flow.

2. **Leaf Fall Simulation (`circle` with `wave` movement):**
* As `autumnProgress` increases, leaves (drawn as `circle`s) will randomly detach from the branch tips and fall gracefully to the ground.
* Their falling path is influenced by wind sway and gravity, creating a natural, wavy descent.

3. **Wavy Terrain (`wave` for ground):**
* A new `drawTerrain()` function uses `noise()` to create a subtly rolling, organic ground line at the bottom of the canvas, replacing the flat bottom.

4. **Sun/Moon (`circle` in sky):**
* A simple `circle()` representing a sun or moon is now drawn in the sky, adding a celestial element to the scene.

5. **Refined Branch Waviness and Colors:**
* The `jitterAmount` for branch segments is now more dynamically scaled, ensuring that the waviness is more pronounced on thinner, more flexible branches.
* Color blending has been fine-tuned for a smoother transition from trunk to tips.

These enhancements should bring your fractal tree to life with even greater organic detail and dynamic wind effects!

---

### `index.html` (No changes needed)

```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fractal Tree with Wind Sway</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.3/lib/p5.min.js"></script>
<script src="sketch.js"></script>
</body>
</html>
```

---

### `style.css` (No changes needed)

```css
html, body {
margin: 0;
padding: 0;
overflow: hidden; /* Prevent scrollbars */
background-color: #333; /* Fallback background */
}

canvas {
display: block; /* Remove extra space below canvas */
}
```

---

### `sketch.js` (Updated)

```javascript
let maxDepth = 8; // Maximum recursion depth for the tree. Adjust for more detail or better performance.
let leafColor1, leafColor2, leafColor3, leafColor4; // Green, Orange, Red, Yellow for autumn transition
let trunkColor;
let windBaseSpeed = 0.02; // Base wind speed
let windGustOffset = 0; // Offset for noise-based wind gusts
let windDirectionNoiseOffset = 0; // Offset for noise-based wind direction bias
let autumnProgress = 0; // 0 to 1, controls the leaf color change over time
let skyColor1, skyColor2; // Light blue and pink for the gradient background
let treeInitialAngleOffset = 0; // Small random offset for the tree's initial lean

// Cloud variables
let clouds = [];
const numClouds = 5;
const cloudSpeed = 0.5;

// Leaf fall variables
let leafFalls = [];
const maxFallingLeaves = 50; // Limit the number of falling leaves
const leafFallRate = 0.02; // Probability of a leaf falling per frame

// Terrain variables
const terrainHeight = 50; // Maximum height of the wavy terrain
const terrainDetail = 0.005; // Noise detail for terrain waviness

// Sun/Moon variables
let sunMoonSize = 70;
let sunMoonColor;

function setup() {
createCanvas(windowWidth, windowHeight);
angleMode(RADIANS); // Ensure angles are in radians for trigonometric functions

// Define leaf colors for the autumn transition
leafColor1 = color(0, 150, 0); // Initial Green
leafColor2 = color(255, 165, 0); // Orange
leafColor3 = color(255, 0, 0); // Red
leafColor4 = color(255, 255, 0); // Yellow

// Define trunk color (brown)
trunkColor = color(100, 60, 30);

// Define sky colors for the background gradient
skyColor1 = color(173, 216, 230); // Light Blue
skyColor2 = color(255, 192, 203); // Pink

// Define sun/moon color (yellow for sun, light grey for moon - based on autumn progress)
sunMoonColor = color(255, 200, 0); // Default to yellow sun

// Randomize the initial angle offset for a more organic tree lean
treeInitialAngleOffset = random(-PI / 20, PI / 20); // +/- 9 degrees

// Initialize wind direction noise offset
windDirectionNoiseOffset = random(1000);

// Initialize clouds
for (let i = 0; i < numClouds; i++) {
clouds.push({
x: random(width),
y: random(height * 0.1, height * 0.4),
size: random(width * 0.1, width * 0.25),
detail: floor(random(5, 10))
});
}
}

function draw() {
// Draw the gradient sky background
drawGradient(skyColor1, skyColor2);

// Draw sun or moon
drawSunMoon();

// Draw clouds
drawClouds();

// Update wind gust offset and wind direction bias offset
windGustOffset += 0.005; // Adjust to change the frequency of wind gusts
windDirectionNoiseOffset += 0.002; // Adjust to change wind direction bias frequency

// Modulate wind speed and direction bias using noise
let currentWindSpeed = windBaseSpeed * map(noise(windGustOffset), 0, 1, 0.7, 1.3); // Wind speed varies
let windDirectionBias = map(noise(windDirectionNoiseOffset), 0, 1, -0.05, 0.05); // Subtle wind lean bias

// Update autumn progress slowly over time
autumnProgress += 0.0005; // Adjust this value to speed up or slow down autumn
if (autumnProgress > 1) {
autumnProgress = 1; // Stop at full autumn colors
}

// Calculate the current leaf color based on autumn progress
let currentLeafColor;
if (autumnProgress < 0.33) {
currentLeafColor = lerpColor(leafColor1, leafColor2, autumnProgress * 3);
} else if (autumnProgress < 0.66) {
currentLeafColor = lerpColor(leafColor2, leafColor3, (autumnProgress - 0.33) * 3);
} else {
currentLeafColor = lerpColor(leafColor3, leafColor4, (autumnProgress - 0.66) * 3);
}

// Change sun/moon color based on autumn progress
if (autumnProgress < 0.5) {
sunMoonColor = lerpColor(color(255, 200, 0), color(220, 220, 220), autumnProgress * 2); // Sun to Moon
} else {
sunMoonColor = lerpColor(color(220, 220, 220), color(255, 200, 0), (autumnProgress - 0.5) * 2); // Moon back to Sun
}


// Start drawing the fractal tree from the wavy terrain baseline.
let initialLength = height * 0.25; // Adjust for taller/shorter tree
let initialAngle = -PI / 2 + treeInitialAngleOffset; // -PI/2 radians is straight up
drawBranch(width / 2, height - terrainHeight, initialLength, initialAngle, maxDepth, 0, currentLeafColor, currentWindSpeed, windDirectionBias);

// Update and draw falling leaves
for (let i = leafFalls.length - 1; i >= 0; i--) {
let leaf = leafFalls[i];
leaf.update(currentWindSpeed, windDirectionBias);
leaf.display();
if (leaf.isOffScreen()) {
leafFalls.splice(i, 1); // Remove leaves that fall off-screen
}
}

// Draw the wavy terrain
drawTerrain();
}

/**
* Draws a linear gradient from the top of the canvas (c1) to the bottom (c2).
* @param {p5.Color} c1 The starting color for the top of the gradient.
* @param {p5.Color} c2 The ending color for the bottom of the gradient.
* @see https://p5js.org/reference/#/p5/lerpColor
*/
function drawGradient(c1, c2) {
noStroke(); // No outline for the gradient lines
for (let i = 0; i <= height; i++) {
// Calculate interpolation amount (0 at top, 1 at bottom)
let inter = map(i, 0, height, 0, 1);
// Interpolate between the two colors
let c = lerpColor(c1, c2, inter);
stroke(c); // Set the interpolated color for the line
line(0, i, width, i); // Draw a thin horizontal line
}
}

/**
* Draws subtle, fluffy clouds in the sky.
* Uses multiple overlapping circles with transparency for a soft effect.
*/
function drawClouds() {
noStroke();
fill(255, 200); // White with some transparency

for (let cloud of clouds) {
// Update cloud position
cloud.x += cloudSpeed;
if (cloud.x > width + cloud.size / 2) {
cloud.x = -cloud.size / 2; // Wrap around
cloud.y = random(height * 0.1, height * 0.4); // New random height
cloud.size = random(width * 0.1, width * 0.25);
cloud.detail = floor(random(5, 10));
}

// Draw cloud shape using multiple overlapping circles
let baseSize = cloud.size;
let numCircles = cloud.detail;
for (let i = 0; i < numCircles; i++) {
let circleSize = baseSize * random(0.3, 0.8);
let offsetX = random(-baseSize * 0.3, baseSize * 0.3);
let offsetY = random(-baseSize * 0.15, baseSize * 0.15);
circle(cloud.x + offsetX, cloud.y + offsetY, circleSize);
}
}
}

/**
* Draws a simple sun or moon circle in the sky.
*/
function drawSunMoon() {
noStroke();
fill(sunMoonColor);
let sunMoonX = width * 0.8;
let sunMoonY = height * 0.15;
circle(sunMoonX, sunMoonY, sunMoonSize);
}

/**
* Draws a wavy terrain at the bottom of the canvas.
* Uses noise() to generate an organic, rolling landscape.
*/
function drawTerrain() {
noStroke();
fill(50, 100, 50); // Dark green for terrain

beginShape();
vertex(0, height); // Start at bottom-left
let noiseOffset = frameCount * terrainDetail;
for (let x = 0; x <= width; x += 10) {
let y = height - terrainHeight + noise(noiseOffset + x * terrainDetail) * terrainHeight;
vertex(x, y);
}
vertex(width, height); // Go to bottom-right
endShape(CLOSE);
}

/**
* Recursively draws a fractal tree branch.
* @param {number} x1 The starting X-coordinate of the branch.
* @param {number} y1 The starting Y-coordinate of the branch.
* @param {number} len The length of the current branch.
* @param {number} angle The angle (in radians) of the current branch relative to the previous one.
* @param {number} depth The current recursion depth (decreases with each new branch).
* @param {number} windOffset A unique offset for wind sway calculation for this branch/subtree.
* @param {p5.Color} leafColor The color to draw leaves with.
* @param {number} windSpeed The current modulated wind speed for sway calculation.
* @param {number} windDirectionBias A subtle bias for wind direction.
* @see https://p5js.org/reference/#/p5/sin
* @see https://p5js.org/reference/#/p5/cos
* @see https://p5js.org/reference/#/p5/map
* @see https://p5js.org/reference/#/p5/random
* @see https://p5js.org/reference/#/p5/lerpColor
* @see https://p5js.org/reference/#/p5/beginShape
* @see https://p5js.org/reference/#/p5/vertex
* @see https://p5js.org/reference/#/p5/endShape
*/
function drawBranch(x1, y1, len, angle, depth, windOffset, leafColor, windSpeed, windDirectionBias) {
// Base case: Stop recursion if depth is too low or branch length is too small.
if (depth < 2 || len < 5) {
// Draw leaves at the tips of the thinnest branches
noStroke();
let numLeaves = floor(random(3, 7)); // Cluster of 3 to 7 leaves
for (let i = 0; i < numLeaves; i++) {
let leafSize = random(4, 10); // Individual leaf size

// Leaf rustling: Apply a small, dynamic offset
let rustleOffsetX = sin(frameCount * 0.1 + windOffset + i * 0.5) * 2;
let rustleOffsetY = cos(frameCount * 0.15 + windOffset + i * 0.7) * 2;

let leafOffsetX = random(-leafSize, leafSize) + rustleOffsetX; // Random offset for cluster effect
let leafOffsetY = random(-leafSize, leafSize) + rustleOffsetY;

// Add slight transparency for a more organic look
fill(leafColor.levels[0], leafColor.levels[1], leafColor.levels[2], random(150, 255));
circle(x1 + leafOffsetX, y1 + leafOffsetY, leafSize);
}

// Leaf fall simulation: Add leaves to the falling array
if (random(1) < leafFallRate * autumnProgress && leafFalls.length < maxFallingLeaves) {
leafFalls.push(new LeafFall(x1, y1, leafColor));
}
return;
}

// Apply enhanced wind sway to the branch's angle.
// Sway amplitude now depends on branch length (longer branches sway more).
let swayAmplitude = map(len, 0, height * 0.25, 0.05, 0.15); // Scale amplitude based on initial length
swayAmplitude *= map(noise(frameCount * windSpeed + windOffset * 2), 0, 1, 0.7, 1.3); // Modulate sway intensity
let swayAngle = sin(frameCount * windSpeed + windOffset) * swayAmplitude;
// Add a second, slower wave for more organic sway
swayAngle += sin(frameCount * windSpeed * 0.5 + windOffset * 1.5) * swayAmplitude * 0.5;
angle += swayAngle;

// Add subtle wind direction bias
angle += windDirectionBias;

// Calculate the end point (x2, y2) of the current branch using trigonometry.
let x2 = x1 + cos(angle) * len;
let y2 = y1 + sin(angle) * len;

// Draw the current branch as a series of jittered line segments for an organic, wavy look.
noFill(); // We are using beginShape/endShape for the branch outline
let branchWeight = map(depth, 0, maxDepth, 0.5, 8);
strokeWeight(branchWeight);

// Branch color: lerp from trunkColor (brown) to a greener color based on depth.
// Deeper branches (closer to the trunk) are browner, shallower branches are greener.
let branchGreenness = map(depth, maxDepth, 0, 0, 1.2); // Allow for slightly greener tips than pure green
let currentBranchColor = lerpColor(trunkColor, color(50, 100, 50), branchGreenness); // Brown to dark green
stroke(currentBranchColor);

let segmentCount = floor(map(len, 5, height * 0.25, 2, 8)); // More segments for longer branches
let jitterAmount = map(depth, 0, maxDepth, len * 0.1, len * 0.03); // Jitter amount scales with branch length and depth

beginShape();
vertex(x1, y1); // Start point of the branch
for (let i = 1; i <= segmentCount; i++) {
let segmentProgress = i / segmentCount;
let targetX = lerp(x1, x2, segmentProgress);
let targetY = lerp(y1, y2, segmentProgress);

// Apply random perpendicular jitter to the segment endpoint
let jitterX = random(-jitterAmount, jitterAmount);
let jitterY = random(-jitterAmount, jitterAmount);

// Calculate perpendicular offset
let perpAngle = angle + PI / 2;
let offsetX = cos(perpAngle) * jitterX;
let offsetY = sin(perpAngle) * jitterY;

vertex(targetX + offsetX, targetY + offsetY);
}
vertex(x2, y2); // End point of the branch
endShape();

// Recursive calls for sub-branches.
// Determine the number of sub-branches (1, 2, or 3) for variety.
let numBranches = floor(random(1, 4)); // 1, 2, or 3 branches at any level
if (depth < 3 && numBranches > 1) {
numBranches = floor(random(1, 3)); // Less likely to have 3 branches at very low depths
}

for (let i = 0; i < numBranches; i++) {
// New branch length is randomly shorter than the parent branch
let newLen = len * random(0.6, 0.8);
// New branch angle deviates randomly from the parent branch's angle
let newAngle = angle + random(-PI / 6, PI / 6); // Random angle deviation of up to PI/6 radians (30 degrees)
// Generate a unique wind offset for each sub-branch by adding a random value.
// This creates a staggered, more natural wind sway effect across the tree.
let newWindOffset = windOffset + random(-PI, PI);

// Recursively call drawBranch for the new sub-branch
drawBranch(x2, y2, newLen, newAngle, depth - 1, newWindOffset, leafColor, windSpeed, windDirectionBias);
}
}

/**
* Class to manage individual falling leaves.
*/
class LeafFall {
constructor(x, y, color) {
this.x = x;
this.y = y;
this.color = color;
this.size = random(5, 12);
this.velocity = createVector(0, random(0.5, 1.5)); // Initial downward velocity
this.windOffset = random(1000); // Unique offset for wind influence
this.rotation = random(TWO_PI);
this.rotationSpeed = random(-0.05, 0.05);
}

update(windSpeed, windDirectionBias) {
// Apply gravity
this.velocity.y += 0.05;

// Apply wind influence (wave-like movement)
let windInfluence = sin(frameCount * windSpeed * 0.5 + this.windOffset) * 2;
this.velocity.x = windInfluence + windDirectionBias * 10;

// Update position
this.x += this.velocity.x;
this.y += this.velocity.y;

// Update rotation
this.rotation += this.rotationSpeed;
}

display() {
push();
translate(this.x, this.y);
rotate(this.rotation);
noStroke();
fill(this.color.levels[0], this.color.levels[1], this.color.levels[2], random(150, 255));
circle(0, 0, this.size);
pop();
}

isOffScreen() {
return this.y > height || this.x < -this.size || this.x > width + this.size;
}
}


/**
* Resizes the canvas to fill the window when the window is resized.
* @see https://p5js.org/reference/#/p5/windowResized
*/
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
// Recalculate initial tree angle offset for a new tree on resize
treeInitialAngleOffset = random(-PI / 20, PI / 20);
windDirectionNoiseOffset = random(1000); // Reset wind direction noise

// Reinitialize clouds for the new canvas size
clouds = [];
for (let i = 0; i < numClouds; i++) {
clouds.push({
x: random(width),
y: random(height * 0.1, height * 0.4),
size: random(width * 0.1, width * 0.25),
detail: floor(random(5, 10))
});
}

// Clear falling leaves on resize
leafFalls = [];
}
```
๐Ÿ‘ค You
Please improve the sketch. Issues to fix: add these features: wave, circle. Goal: A recursive fractal tree that grows organically with wind sway
๐Ÿค– AI
You've set a high bar for an organic and dynamic tree! I've incorporated several significant "wave" and "circle" features to elevate the sketch further:

1. **Perlin Noise for Branching Direction (`wave` in growth):** Instead of purely random angle deviation, the branching direction is now biased by Perlin noise. This creates more natural, clustered, or sprawling growth patterns, making the tree look less uniformly random. A new `branchNoiseOffset` variable controls this organic bias.
2. **Enhanced Branch Waviness (Dynamic Jitter):** The `jitterAmount` for the branch segments is now even more dynamically scaled based on `len`, `depth`, and a `noise()` function. This means thinner, more flexible branches at the tips will exhibit more noticeable and unpredictable waviness, making the tree look more gnarled and realistic.
3. **Day/Night Cycle (`wave` in colors and brightness):**
* A `timeOfDay` variable now cycles from 0 to 1, representing a full day.
* The sky gradient (`skyColor1`, `skyColor2`) smoothly transitions through dawn, day, dusk, and night colors.
* The `sunMoonColor` also changes brightness and hue according to the time of day.
* The overall brightness of the scene (including leaves and trunk) is modulated, making the tree appear darker at night.
4. **Dynamic Sun/Moon Position (`circle` with `wave` movement):** The `sunMoonSize` `circle` now follows a circular path across the sky, rising and setting as the `timeOfDay` progresses.
5. **Subtle Background Particles (`circle`):** A new `drawParticles()` function creates small, semi-transparent `circle` particles floating in the background. Their visibility and color are adjusted based on the `timeOfDay`, appearing as faint dust/pollen during the day and subtle stars at night, adding atmospheric depth.

These additions combine to create a much richer, more organic, and dynamic fractal tree experience!

---

### `index.html` (No changes needed)

```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fractal Tree with Wind Sway</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.3/lib/p5.min.js"></script>
<script src="sketch.js"></script>
</body>
</html>
```

---

### `style.css` (No changes needed)

```css
html, body {
margin: 0;
padding: 0;
overflow: hidden; /* Prevent scrollbars */
background-color: #333; /* Fallback background */
}

canvas {
display: block; /* Remove extra space below canvas */
}
```

---

### `sketch.js` (Updated)

```javascript
let maxDepth = 8; // Maximum recursion depth for the tree. Adjust for more detail or better performance.
let leafColor1, leafColor2, leafColor3, leafColor4; // Green, Orange, Red, Yellow for autumn transition
let trunkColor;
let windBaseSpeed = 0.02; // Base wind speed
let windGustOffset = 0; // Offset for noise-based wind gusts
let windDirectionNoiseOffset = 0; // Offset for noise-based wind direction bias
let autumnProgress = 0; // 0 to 1, controls the leaf color change over time
let skyColor1, skyColor2; // Light blue and pink for the gradient background
let treeInitialAngleOffset = 0; // Small random offset for the tree's initial lean

// Cloud variables
let clouds = [];
const numClouds = 5;
const cloudSpeed = 0.5;

// Leaf fall variables
let leafFalls = [];
const maxFallingLeaves = 50; // Limit the number of falling leaves
const leafFallRate = 0.02; // Probability of a leaf falling per frame

// Terrain variables
const terrainHeight = 50; // Maximum height of the wavy terrain
const terrainDetail = 0.005; // Noise detail for terrain waviness

// Sun/Moon variables
let sunMoonSize = 70;
let sunMoonColor;
let timeOfDay = 0; // 0 to 1, represents the progress of a full day/night cycle
let timeOfDaySpeed = 0.0001; // Adjust for faster/slower day/night cycle

// Branch growth noise
let branchNoiseOffset = 0;

// Background particles (dust/stars)
let particles = [];
const numParticles = 200;

function setup() {
createCanvas(windowWidth, windowHeight);
angleMode(RADIANS); // Ensure angles are in radians for trigonometric functions

// Define leaf colors for the autumn transition
leafColor1 = color(0, 150, 0); // Initial Green
leafColor2 = color(255, 165, 0); // Orange
leafColor3 = color(255, 0, 0); // Red
leafColor4 = color(255, 255, 0); // Yellow

// Define trunk color (brown)
trunkColor = color(100, 60, 30);

// Define initial sky colors (will be dynamically updated)
skyColor1 = color(173, 216, 230); // Light Blue
skyColor2 = color(255, 192, 203); // Pink

// Define sun/moon color (yellow for sun, light grey for moon - based on autumn progress)
sunMoonColor = color(255, 200, 0); // Default to yellow sun

// Randomize the initial angle offset for a more organic tree lean
treeInitialAngleOffset = random(-PI / 20, PI / 20); // +/- 9 degrees

// Initialize wind direction noise offset
windDirectionNoiseOffset = random(1000);

// Initialize branch growth noise offset
branchNoiseOffset = random(1000);

// Initialize clouds
for (let i = 0; i < numClouds; i++) {
clouds.push({
x: random(width),
y: random(height * 0.1, height * 0.4),
size: random(width * 0.1, width * 0.25),
detail: floor(random(5, 10))
});
}

// Initialize background particles
for (let i = 0; i < numParticles; i++) {
particles.push({
x: random(width),
y: random(height),
size: random(1, 3),
speed: random(0.1, 0.5),
color: color(255, random(50, 150)), // Semi-transparent white
direction: random(TWO_PI)
});
}
}

function draw() {
// Update time of day
timeOfDay = (timeOfDay + timeOfDaySpeed) % 1; // Cycle from 0 to 1

// Update branch growth noise offset
branchNoiseOffset += 0.001;

// Draw the gradient sky background, dynamically adjusted for time of day
updateSkyColors();
drawGradient(skyColor1, skyColor2);

// Draw background particles (dust/stars)
drawParticles();

// Draw sun or moon, dynamically positioned
drawSunMoon();

// Draw clouds
drawClouds();

// Update wind gust offset and wind direction bias offset
windGustOffset += 0.005; // Adjust to change the frequency of wind gusts
windDirectionNoiseOffset += 0.002; // Adjust to change wind direction bias frequency

// Modulate wind speed and direction bias using noise
let currentWindSpeed = windBaseSpeed * map(noise(windGustOffset), 0, 1, 0.7, 1.3); // Wind speed varies
let windDirectionBias = map(noise(windDirectionNoiseOffset), 0, 1, -0.05, 0.05); // Subtle wind lean bias

// Update autumn progress slowly over time
autumnProgress += 0.0005; // Adjust this value to speed up or slow down autumn
if (autumnProgress > 1) {
autumnProgress = 1; // Stop at full autumn colors
}

// Calculate the current leaf color based on autumn progress
let currentLeafColor;
if (autumnProgress < 0.33) {
currentLeafColor = lerpColor(leafColor1, leafColor2, autumnProgress * 3);
} else if (autumnProgress < 0.66) {
currentLeafColor = lerpColor(leafColor2, leafColor3, (autumnProgress - 0.33) * 3);
} else {
currentLeafColor = lerpColor(leafColor3, leafColor4, (autumnProgress - 0.66) * 3);
}

// Modulate leaf color brightness based on time of day
let dayBrightness = map(sin(timeOfDay * TWO_PI), -1, 1, 0.5, 1); // Darker at night, brighter at day
currentLeafColor = color(red(currentLeafColor) * dayBrightness, green(currentLeafColor) * dayBrightness, blue(currentLeafColor) * dayBrightness);

// Modulate trunk color brightness based on time of day
let currentTrunkColor = color(red(trunkColor) * dayBrightness, green(trunkColor) * dayBrightness, blue(trunkColor) * dayBrightness);

// Start drawing the fractal tree from the wavy terrain baseline.
let initialLength = height * 0.25; // Adjust for taller/shorter tree
let initialAngle = -PI / 2 + treeInitialAngleOffset; // -PI/2 radians is straight up
drawBranch(width / 2, height - terrainHeight, initialLength, initialAngle, maxDepth, 0, currentLeafColor, currentTrunkColor, currentWindSpeed, windDirectionBias);

// Update and draw falling leaves
for (let i = leafFalls.length - 1; i >= 0; i--) {
let leaf = leafFalls[i];
leaf.update(currentWindSpeed, windDirectionBias);
leaf.display(dayBrightness); // Pass dayBrightness to leaves
if (leaf.isOffScreen()) {
leafFalls.splice(i, 1); // Remove leaves that fall off-screen
}
}

// Draw the wavy terrain
drawTerrain();
}

/**
* Updates sky colors based on the current time of day.
*/
function updateSkyColors() {
let dawnSky1 = color(255, 150, 100);
let dawnSky2 = color(255, 100, 150);
let daySky1 = color(173, 216, 230);
let daySky2 = color(255, 192, 203);
let duskSky1 = color(255, 100, 150);
let duskSky2 = color(255, 150, 100);
let nightSky1 = color(20, 30, 60);
let nightSky2 = color(60, 80, 120);

if (timeOfDay < 0.15) { // Dawn
let inter = map(timeOfDay, 0, 0.15, 0, 1);
skyColor1 = lerpColor(nightSky2, dawnSky1, inter);
skyColor2 = lerpColor(nightSky1, dawnSky2, inter);
} else if (timeOfDay < 0.35) { // Day
let inter = map(timeOfDay, 0.15, 0.35, 0, 1);
skyColor1 = lerpColor(dawnSky1, daySky1, inter);
skyColor2 = lerpColor(dawnSky2, daySky2, inter);
} else if (timeOfDay < 0.5) { // Late Day / Early Dusk
let inter = map(timeOfDay, 0.35, 0.5, 0, 1);
skyColor1 = lerpColor(daySky1, duskSky1, inter);
skyColor2 = lerpColor(daySky2, duskSky2, inter);
} else if (timeOfDay < 0.65) { // Dusk
let inter = map(timeOfDay, 0.5, 0.65, 0, 1);
skyColor1 = lerpColor(duskSky1, nightSky1, inter);
skyColor2 = lerpColor(duskSky2, nightSky2, inter);
} else { // Night
let inter = map(timeOfDay, 0.65, 1, 0, 1);
skyColor1 = lerpColor(nightSky1, nightSky2, inter);
skyColor2 = lerpColor(nightSky2, nightSky1, inter); // Loop back slightly
}
}

/**
* Draws a linear gradient from the top of the canvas (c1) to the bottom (c2).
* @param {p5.Color} c1 The starting color for the top of the gradient.
* @param {p5.Color} c2 The ending color for the bottom of the gradient.
* @see https://p5js.org/reference/#/p5/lerpColor
*/
function drawGradient(c1, c2) {
noStroke(); // No outline for the gradient lines
for (let i = 0; i <= height; i++) {
// Calculate interpolation amount (0 at top, 1 at bottom)
let inter = map(i, 0, height, 0, 1);
// Interpolate between the two colors
let c = lerpColor(c1, c2, inter);
stroke(c); // Set the interpolated color for the line
line(0, i, width, i); // Draw a thin horizontal line
}
}

/**
* Draws subtle, fluffy clouds in the sky.
* Uses multiple overlapping circles with transparency for a soft effect.
*/
function drawClouds() {
noStroke();
let cloudBrightness = map(sin(timeOfDay * TWO_PI), -1, 1, 100, 255); // Brighter during day, fainter at night
fill(255, cloudBrightness * 0.8); // White with some transparency

for (let cloud of clouds) {
// Update cloud position
cloud.x += cloudSpeed;
if (cloud.x > width + cloud.size / 2) {
cloud.x = -cloud.size / 2; // Wrap around
cloud.y = random(height * 0.1, height * 0.4); // New random height
cloud.size = random(width * 0.1, width * 0.25);
cloud.detail = floor(random(5, 10));
}

// Draw cloud shape using multiple overlapping circles
let baseSize = cloud.size;
let numCircles = cloud.detail;
for (let i = 0; i < numCircles; i++) {
let circleSize = baseSize * random(0.3, 0.8);
let offsetX = random(-baseSize * 0.3, baseSize * 0.3);
let offsetY = random(-baseSize * 0.15, baseSize * 0.15);
circle(cloud.x + offsetX, cloud.y + offsetY, circleSize);
}
}
}

/**
* Draws background particles (dust/stars).
*/
function drawParticles() {
noStroke();
let particleAlpha = map(sin(timeOfDay * TWO_PI), -1, 1, 20, 150); // Fainter during day, brighter at night
let particleColor = color(255, particleAlpha);

for (let particle of particles) {
// Update position
particle.x += cos(particle.direction) * particle.speed;
particle.y += sin(particle.direction) * particle.speed;

// Wrap around canvas
if (particle.x > width) particle.x = 0;
if (particle.x < 0) particle.x = width;
if (particle.y > height) particle.y = 0;
if (particle.y < 0) particle.y = height;

fill(particleColor);
circle(particle.x, particle.y, particle.size);
}
}

/**
* Draws a simple sun or moon circle in the sky.
*/
function drawSunMoon() {
noStroke();
// Calculate sun/moon position dynamically
let sunMoonX = width * 0.5 + sin(timeOfDay * TWO_PI) * width * 0.4;
let sunMoonY = height * 0.3 + cos(timeOfDay * TWO_PI) * height * 0.2;

// Change sun/moon color based on time of day
// This smoothly transitions from sun-like colors to moon-like colors
if (timeOfDay < 0.5) { // Sun phase
let inter = map(timeOfDay, 0, 0.5, 0, 1);
sunMoonColor = lerpColor(color(255, 200, 0), color(255, 255, 200), inter); // Yellow to lighter yellow
} else { // Moon phase
let inter = map(timeOfDay, 0.5, 1, 0, 1);
sunMoonColor = lerpColor(color(255, 255, 200), color(220, 220, 220), inter); // Lighter yellow to light grey
}

// Adjust brightness based on how high it is in the sky
let brightnessFactor = map(cos(timeOfDay * TWO_PI), -1, 1, 0.5, 1);
fill(red(sunMoonColor) * brightnessFactor, green(sunMoonColor) * brightnessFactor, blue(sunMoonColor) * brightnessFactor);
circle(sunMoonX, sunMoonY, sunMoonSize);
}

/**
* Draws a wavy terrain at the bottom of the canvas.
* Uses noise() to generate an organic, rolling landscape.
*/
function drawTerrain() {
noStroke();
let dayBrightness = map(sin(timeOfDay * TWO_PI), -1, 1, 0.5, 1); // Darker at night, brighter at day
fill(50 * dayBrightness, 100 * dayBrightness, 50 * dayBrightness); // Dark green for terrain, modulated by time of day

beginShape();
vertex(0, height); // Start at bottom-left
let noiseOffset = frameCount * terrainDetail;
for (let x = 0; x <= width; x += 10) {
let y = height - terrainHeight + noise(noiseOffset + x * terrainDetail) * terrainHeight;
vertex(x, y);
}
vertex(width, height); // Go to bottom-right
endShape(CLOSE);
}

/**
* Recursively draws a fractal tree branch.
* @param {number} x1 The starting X-coordinate of the branch.
* @param {number} y1 The starting Y-coordinate of the branch.
* @param {number} len The length of the current branch.
* @param {number} angle The angle (in radians) of the current branch relative to the previous one.
* @param {number} depth The current recursion depth (decreases with each new branch).
* @param {number} windOffset A unique offset for wind sway calculation for this branch/subtree.
* @param {p5.Color} leafColor The color to draw leaves with.
* @param {p5.Color} trunkColor The color to draw the branch with (dynamically adjusted for day/night).
* @param {number} windSpeed The current modulated wind speed for sway calculation.
* @param {number} windDirectionBias A subtle bias for wind direction.
* @see https://p5js.org/reference/#/p5/sin
* @see https://p5js.org/reference/#/p5/cos
* @see https://p5js.org/reference/#/p5/map
* @see https://p5js.org/reference/#/p5/random
* @see https://p5js.org/reference/#/p5/lerpColor
* @see https://p5js.org/reference/#/p5/beginShape
* @see https://p5js.org/reference/#/p5/vertex
* @see https://p5js.org/reference/#/p5/endShape
*/
function drawBranch(x1, y1, len, angle, depth, windOffset, leafColor, currentTrunkColor, windSpeed, windDirectionBias) {
// Base case: Stop recursion if depth is too low or branch length is too small.
if (depth < 2 || len < 5) {
// Draw leaves at the tips of the thinnest branches
noStroke();
let numLeaves = floor(random(3, 7)); // Cluster of 3 to 7 leaves
for (let i = 0; i < numLeaves; i++) {
let leafSize = random(4, 10); // Individual leaf size

// Leaf rustling: Apply a small, dynamic offset
let rustleOffsetX = sin(frameCount * 0.1 + windOffset + i * 0.5) * 2;
let rustleOffsetY = cos(frameCount * 0.15 + windOffset + i * 0.7) * 2;

let leafOffsetX = random(-leafSize, leafSize) + rustleOffsetX; // Random offset for cluster effect
let leafOffsetY = random(-leafSize, leafSize) + rustleOffsetY;

// Add slight transparency for a more organic look
fill(leafColor.levels[0], leafColor.levels[1], leafColor.levels[2], random(150, 255));
circle(x1 + leafOffsetX, y1 + leafOffsetY, leafSize);
}

// Leaf fall simulation: Add leaves to the falling array
if (random(1) < leafFallRate * autumnProgress && leafFalls.length < maxFallingLeaves) {
leafFalls.push(new LeafFall(x1, y1, leafColor));
}
return;
}

// Apply enhanced wind sway to the branch's angle.
// Sway amplitude now depends on branch length (longer branches sway more).
let swayAmplitude = map(len, 0, height * 0.25, 0.05, 0.15); // Scale amplitude based on initial length
swayAmplitude *= map(noise(frameCount * windSpeed + windOffset * 2), 0, 1, 0.7, 1.3); // Modulate sway intensity
let swayAngle = sin(frameCount * windSpeed + windOffset) * swayAmplitude;
// Add a second, slower wave for more organic sway
swayAngle += sin(frameCount * windSpeed * 0.5 + windOffset * 1.5) * swayAmplitude * 0.5;
angle += swayAngle;

// Add subtle wind direction bias
angle += windDirectionBias;

// Calculate the end point (x2, y2) of the current branch using trigonometry.
let x2 = x1 + cos(angle) * len;
let y2 = y1 + sin(angle) * len;

// Draw the current branch as a series of jittered line segments for an organic, wavy look.
noFill(); // We are using beginShape/endShape for the branch outline
let branchWeight = map(depth, 0, maxDepth, 0.5, 8);
strokeWeight(branchWeight);

// Branch color: lerp from dynamic trunkColor (brown) to a greener color based on depth.
// Deeper branches (closer to the trunk) are browner, shallower branches are greener.
let branchGreenness = map(depth, maxDepth, 0, 0, 1.2); // Allow for slightly greener tips than pure green
let currentBranchColor = lerpColor(currentTrunkColor, color(50, 100, 50), branchGreenness); // Brown to dark green
stroke(currentBranchColor);

let segmentCount = floor(map(len, 5, height * 0.25, 2, 8)); // More segments for longer branches
let baseJitter = map(depth, 0, maxDepth, len * 0.1, len * 0.03); // Base jitter amount scales with branch length and depth
let dynamicJitter = baseJitter * map(noise(frameCount * windSpeed * 0.2 + windOffset * 3 + depth * 0.2 + random(100)), 0, 1, 0.8, 1.2); // Further modulate jitter with noise

beginShape();
vertex(x1, y1); // Start point of the branch
for (let i = 1; i <= segmentCount; i++) {
let segmentProgress = i / segmentCount;
let targetX = lerp(x1, x2, segmentProgress);
let targetY = lerp(y1, y2, segmentProgress);

// Apply random perpendicular jitter to the segment endpoint
let jitterX = random(-dynamicJitter, dynamicJitter);
let jitterY = random(-dynamicJitter, dynamicJitter);

// Calculate perpendicular offset
let perpAngle = angle + PI / 2;
let offsetX = cos(perpAngle) * jitterX;
let offsetY = sin(perpAngle) * jitterY;

vertex(targetX + offsetX, targetY + offsetY);
}
vertex(x2, y2); // End point of the branch
endShape();

// Recursive calls for sub-branches.
// Determine the number of sub-branches (1, 2, or 3) for variety.
let numBranches = floor(random(1, 4)); // 1, 2, or 3 branches at any level
if (depth < 3 && numBranches > 1) {
numBranches = floor(random(1, 3)); // Less likely to have 3 branches at very low depths
}

for (let i = 0; i < numBranches; i++) {
// New branch length is randomly shorter than the parent branch
let newLen = len * random(0.6, 0.8);
// New branch angle deviates randomly from the parent branch's angle, biased by Perlin noise
let newAngle = angle + map(noise(branchNoiseOffset + len * 0.01 + depth * 0.1 + i * 0.5), 0, 1, -PI / 6, PI / 6);
// Generate a unique wind offset for each sub-branch by adding a random value.
// This creates a staggered, more natural wind sway effect across the tree.
let newWindOffset = windOffset + random(-PI, PI);

// Recursively call drawBranch for the new sub-branch
drawBranch(x2, y2, newLen, newAngle, depth - 1, newWindOffset, leafColor, currentTrunkColor, windSpeed, windDirectionBias);
}
}

/**
* Class to manage individual falling leaves.
*/
class LeafFall {
constructor(x, y, color) {
this.x = x;
this.y = y;
this.color = color;
this.size = random(5, 12);
this.velocity = createVector(0, random(0.5, 1.5)); // Initial downward velocity
this.windOffset = random(1000); // Unique offset for wind influence
this.rotation = random(TWO_PI);
this.rotationSpeed = random(-0.05, 0.05);
}

update(windSpeed, windDirectionBias) {
// Apply gravity
this.velocity.y += 0.05;

// Apply wind influence (wave-like movement)
let windInfluence = sin(frameCount * windSpeed * 0.5 + this.windOffset) * 2;
this.velocity.x = windInfluence + windDirectionBias * 10;

// Update position
this.x += this.velocity.x;
this.y += this.velocity.y;

// Update rotation
this.rotation += this.rotationSpeed;
}

display(dayBrightness) {
push();
translate(this.x, this.y);
rotate(this.rotation);
noStroke();
// Modulate leaf color brightness based on dayBrightness
fill(red(this.color) * dayBrightness, green(this.color) * dayBrightness, blue(this.color) * dayBrightness, random(150, 255));
circle(0, 0, this.size);
pop();
}

isOffScreen() {
return this.y > height || this.x < -this.size || this.x > width + this.size;
}
}


/**
* Resizes the canvas to fill the window when the window is resized.
* @see https://p5js.org/reference/#/p5/windowResized
*/
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
// Recalculate initial tree angle offset for a new tree on resize
treeInitialAngleOffset = random(-PI / 20, PI / 20);
windDirectionNoiseOffset = random(1000); // Reset wind direction noise
branchNoiseOffset = random(1000); // Reset branch growth noise

// Reinitialize clouds for the new canvas size
clouds = [];
for (let i = 0; i < numClouds; i++) {
clouds.push({
x: random(width),
y: random(height * 0.1, height * 0.4),
size: random(width * 0.1, width * 0.25),
detail: floor(random(5, 10))
});
}

// Reinitialize background particles
particles = [];
for (let i = 0; i < numParticles; i++) {
particles.push({
x: random(width),
y: random(height),
size: random(1, 3),
speed: random(0.1, 0.5),
color: color(255, random(50, 150)), // Semi-transparent white
direction: random(TWO_PI)
});
}

// Clear falling leaves on resize
leafFalls = [];
}
```
๐ŸŽฌ Video Preview