It’s fix let’s go but I’m just fixing orange target

This sketch simulates a dynamic ecosystem where blue triangular eaters roam a canvas, seeking and consuming red food items that users place by tapping. When an eater finds food, it displays an orange indicator, travels to it, eats it, then turns red and exits the canvas. Users can spawn additional eaters with a button.

🧪 Try This!

Experiment with the code by making these changes:

  1. Make eaters much slower — Lower the initial maxSpeed range so eaters dawdle toward food instead of rushing.
  2. Spawn more initial eaters — Increase EATER_COUNT to see more competition for food at the start.
  3. Make eaten food disappear instantly — Reduce eatingDuration to 0 so eaters consume food without pausing.
  4. Paint eaters green — Change the eater's default color from blue to green.
  5. Larger spawned food — Food items will be bigger and easier to see when users tap to place them.
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch creates an interactive ecosystem where blue triangle-shaped agents hunt for red circular food. What makes it visually engaging is the orange indicator that appears when an eater claims food, and the dramatic color change to red when they finish eating and leave the canvas. The code uses three key p5.js techniques: steering behaviors (seek and wander) to control movement with smooth acceleration, object-oriented design with Food and Eater classes to manage independent actors, and a state machine to orchestrate each eater's lifecycle—from seeking food through eating and exiting the canvas.

The code is organized into a setup() and draw() loop that manage global arrays of foods and eaters, plus two classes that encapsulate all the behavior of each actor. By reading it, you will learn how to build autonomous agents with multiple behavioral states, how to implement smooth steering with arrival (velocity-damping), how to claim shared resources so agents don't compete for the same target, and how to use a touch event handler to let users place food dynamically on a mobile canvas.

⚙️ How It Works

  1. When the sketch loads, setup() creates a full-window canvas, spawns five initial eaters at random positions, and attaches a touchStarted listener to a spawn button.
  2. Each frame, draw() clears the canvas with a light gray background, then loops through all food items to display them and check if they've been eaten—if so, it removes them from the array and releases their claim.
  3. The eaters are also updated and displayed each frame: each eater evaluates whether it has a valid target food, and if not, searches for the closest unclaimed food and claims it by setting its own targetFood reference and the food's claimedBy reference to itself.
  4. Every eater runs a state machine: if 'seeking', it uses steering behavior (seek with arrival damping) to move smoothly toward claimed food, checking if it has arrived; if 'eating', it counts down a timer and then marks the food eaten and switches to 'leaving' state; if 'leaving', it picks a random canvas edge and speeds up to exit the canvas; if 'wandering', it randomly wanders until food becomes available again.
  5. The orange line drawn from each eater to its claimed food provides visual feedback of the resource-claiming relationship, and the semi-transparent orange circle on claimed food reinforces which eater is targeting it.
  6. When a user taps the canvas (not on the button), touchStarted() creates a new Food at that location; when they tap the green spawn button, spawnNewEater() adds a new Eater to the array. As eaters leave the canvas, they are permanently removed and not replaced—creating a dynamic population that can shrink.

🎓 Concepts You'll Learn

Steering behaviors (seek, wander, arrival damping)State machineResource claiming and shared stateObject-oriented design with classesPhysics integration (velocity and acceleration)Event handling and canvas interactionBackwards loop array removalVector math

📝 Code Breakdown

setup()

setup() runs once when the sketch starts. It's the place to initialize your canvas, create initial objects, and attach event listeners. The windowResized() function at the bottom ensures the canvas stays full-window as the user resizes their browser or rotates their device.

function setup() {
  createCanvas(windowWidth, windowHeight);
  // Create our initial set of eaters
  for (let i = 0; i < EATER_COUNT; i++) {
    eaters.push(new Eater());
  }

  // --- Create and Position the Spawn Button ---
  // Select the button by its ID from index.html
  // See: https://p5js.org/reference/p5/select/
  spawnButton = select('#spawnButton');

  // Attach a touchStarted event listener to the button
  // This works well for both mouse clicks and touch taps
  // See: https://p5js.org/reference/p5/Element/touchStarted/
  spawnButton.touchStarted(spawnNewEater);
}
Line-by-line explanation (5 lines)

🔧 Subcomponents:

function-call Canvas sizing createCanvas(windowWidth, windowHeight);

Creates a full-window canvas that will resize responsively

for-loop Initial eater population for (let i = 0; i < EATER_COUNT; i++) { eaters.push(new Eater()); }

Spawns the initial eaters and adds them to the global array

function-call Button reference spawnButton = select('#spawnButton');

Grabs the HTML button element by its ID so we can attach event listeners to it

function-call Button event listener spawnButton.touchStarted(spawnNewEater);

Wires the button so that tapping it calls spawnNewEater()

createCanvas(windowWidth, windowHeight);
Creates a p5.js canvas that fills the entire window. windowWidth and windowHeight are p5.js global variables that update when the window resizes.
for (let i = 0; i < EATER_COUNT; i++) {
Loops EATER_COUNT times (5 by default) to create the initial population of eaters.
eaters.push(new Eater());
Creates a new Eater object and adds it to the eaters array. The Eater constructor automatically picks a random starting position on the canvas.
spawnButton = select('#spawnButton');
Uses p5.js's select() function to find the HTML element with id='spawnButton' and stores a reference to it so we can listen for clicks/taps.
spawnButton.touchStarted(spawnNewEater);
Attaches a touchStarted event listener to the button element. When the user taps or clicks the button, the spawnNewEater function will be called automatically.

draw()

draw() is p5.js's animation loop—it runs 60 times per second by default. Every frame, you clear the screen (background), update your objects, and redraw them. The backward-loop pattern for removal is critical when managing dynamic arrays of objects. When you remove an eater that has eaten food and left the canvas, you're implementing a form of garbage collection, which is essential for preventing memory leaks in long-running simulations.

function draw() {
  background(220); // A light grey background

  // --- Update and Display Food ---
  // Loop backward to safely remove eaten food items from the array
  for (let i = foods.length - 1; i >= 0; i--) {
    foods[i].display(); // Show the food
    if (foods[i].isEaten()) {
      // Release claim if food was eaten and claimed (should already be null if eaten by this eater)
      if (foods[i].claimedBy) {
        foods[i].claimedBy.targetFood = null; // Clear the target food for the eater that claimed it
        foods[i].claimedBy.status = 'seeking'; // Set eater back to seeking (if they were still seeking this food)
      }
      foods.splice(i, 1); // Remove the food if it's been eaten
    }
  }

  // --- Update and Display Eaters ---
  // Loop backward to safely remove eaters that have left the screen
  for (let i = eaters.length - 1; i >= 0; i--) {
    eaters[i].update(foods); // Update eater's position, seek food, etc.
    eaters[i].display(); // Show the eater

    // If an eater is leaving and has gone off-screen, remove them permanently.
    // They will NOT be replaced automatically.
    if (eaters[i].status === 'leaving' && eaters[i].isOffScreen()) {
      // If this eater had claimed food, release it before being removed
      // This is important if an eater leaves BEFORE eating the food (e.g., food was stolen)
      if (eaters[i].targetFood) {
        eaters[i].targetFood.claimedBy = null;
      }
      eaters.splice(i, 1); // Remove the eater permanently
      // Removed: eaters.push(new Eater()); // NO AUTOMATIC REPLACEMENT
    }
  }
}
Line-by-line explanation (15 lines)

🔧 Subcomponents:

for-loop Food update and display loop for (let i = foods.length - 1; i >= 0; i--) { foods[i].display(); if (foods[i].isEaten()) { if (foods[i].claimedBy) { foods[i].claimedBy.targetFood = null; foods[i].claimedBy.status = 'seeking'; } foods.splice(i, 1); } }

Loops backward through all food items to display them and safely remove eaten ones without breaking the loop

for-loop Eater update and display loop for (let i = eaters.length - 1; i >= 0; i--) { eaters[i].update(foods); eaters[i].display(); if (eaters[i].status === 'leaving' && eaters[i].isOffScreen()) { if (eaters[i].targetFood) { eaters[i].targetFood.claimedBy = null; } eaters.splice(i, 1); } }

Loops backward through all eaters, updates their behavior, displays them, and removes those that have left the canvas

conditional Eaten food cleanup if (foods[i].isEaten()) {

Checks if this food has been eaten and handles removal plus claim release

conditional Off-screen eater removal if (eaters[i].status === 'leaving' && eaters[i].isOffScreen()) {

Checks if the eater is in leaving state and has exited the canvas, then removes them

background(220);
Clears the canvas with a light gray color (220 on a 0–255 scale) each frame. This erases the previous frame's drawings so you see fresh animation, not trails.
for (let i = foods.length - 1; i >= 0; i--) {
Loops backward through the foods array (starting at the last index and counting down). Looping backward is essential: if you loop forward and remove an item, you skip the next item because the indices shift.
foods[i].display();
Calls the display() method on each food object, which draws it as a red circle (and an orange circle if it's been claimed).
if (foods[i].isEaten()) {
Checks whether this food has been eaten (the isEaten() method returns true if the eaten flag is set).
if (foods[i].claimedBy) {
Checks if the food was claimed by an eater. If it was, we need to release that claim before removing the food from the array.
foods[i].claimedBy.targetFood = null;
Clears the eater's target food reference so it stops trying to move toward this now-deleted food.
foods[i].claimedBy.status = 'seeking';
Resets the eater's status back to 'seeking' in case it was still moving toward this food (though normally it would have eaten it first).
foods.splice(i, 1);
Removes the food at index i from the foods array. splice(i, 1) removes 1 element starting at position i.
for (let i = eaters.length - 1; i >= 0; i--) {
Loops backward through the eaters array, updating and displaying each one, and removing those that have exited the canvas.
eaters[i].update(foods);
Calls the update() method on the eater, which re-evaluates food targets, updates position and velocity using steering behaviors, and manages the state machine (seeking → eating → leaving).
eaters[i].display();
Calls the display() method on the eater, which draws it as a blue or red triangle pointing in the direction it's moving.
if (eaters[i].status === 'leaving' && eaters[i].isOffScreen()) {
Checks if the eater is in 'leaving' state (has eaten and is exiting) AND has moved completely off the canvas. If both are true, the eater is removed.
if (eaters[i].targetFood) {
If this exiting eater had claimed a food item, we release that claim so another eater can target it.
eaters[i].targetFood.claimedBy = null;
Clears the claim on the target food, making it available for other eaters to seek.
eaters.splice(i, 1);
Removes the eater from the eaters array permanently. Unlike food, no new eater is spawned to replace it—the population can shrink.

touchStarted()

touchStarted() is a p5.js event handler that fires whenever the user touches or clicks the canvas. The touches array is populated with all active touch points. By checking the button's bounding box, we distinguish between button taps (which should spawn eaters) and canvas taps (which should create food). This is a common pattern for hybrid UI: HTML controls overlaid on a p5.js canvas.

🔬 This condition checks if a touch is inside the button's rectangle. What if you change the && (AND) operators to || (OR)? Would the button respond to touches outside its actual bounds?

    if (touchX >= buttonRect.left && touchX <= buttonRect.right &&
        touchY >= buttonRect.top && touchY <= buttonRect.bottom) {
function touchStarted() {
  // Ensure there's at least one touch
  if (touches.length > 0) {
    let touchX = touches[0].x;
    let touchY = touches[0].y;

    // Get the button's bounding box using getBoundingClientRect()
    // This gives its position and size relative to the viewport
    let buttonRect = spawnButton.elt.getBoundingClientRect();

    // Check if the touch coordinates are within the button's bounding box
    // touches[0].x and touches[0].y are canvas coordinates, which should align
    // with buttonRect.left and buttonRect.top since the canvas is at (0,0)
    if (touchX >= buttonRect.left && touchX <= buttonRect.right &&
        touchY >= buttonRect.top && touchY <= buttonRect.bottom) {
      // If the touch is on the button, let the button's event handler take over
      // and prevent default browser behavior for *this specific touch*.
      // The spawnButton.touchStarted(spawnNewEater) callback will handle spawning.
      return true; 
    }

    // If the touch is NOT on the button, then create a new Food object
    foods.push(new Food(touchX, touchY));
  }

  // For touches that are *not* on the button (i.e., canvas taps),
  // return false to prevent default browser behaviors like scrolling or zooming.
  // See: https://p5js.org/reference/p5/touchStarted/
  return false;
}
Line-by-line explanation (9 lines)

🔧 Subcomponents:

conditional Touch existence check if (touches.length > 0) {

Ensures at least one touch exists before trying to access touches[0]

conditional Button hit detection if (touchX >= buttonRect.left && touchX <= buttonRect.right && touchY >= buttonRect.top && touchY <= buttonRect.bottom) {

Determines if the touch point falls within the button's bounding rectangle

function-call Canvas tap → food spawn foods.push(new Food(touchX, touchY));

Creates a new food object at the touch location if the touch was not on the button

if (touches.length > 0) {
p5.js provides a touches array containing all current touch points. This checks that at least one touch exists before we try to use touches[0].
let touchX = touches[0].x;
Extracts the x-coordinate of the first touch point. touches[0] is an object with x and y properties.
let touchY = touches[0].y;
Extracts the y-coordinate of the first touch point.
let buttonRect = spawnButton.elt.getBoundingClientRect();
Gets the button's position and size relative to the browser window. getBoundingClientRect() returns an object with left, right, top, and bottom properties. The .elt property accesses the underlying HTML element from the p5.Renderer object.
if (touchX >= buttonRect.left && touchX <= buttonRect.right &&
Checks if the touch's x-coordinate falls within the button's left and right edges.
touchY >= buttonRect.top && touchY <= buttonRect.bottom) {
Also checks if the touch's y-coordinate falls within the button's top and bottom edges. If all four conditions are true, the touch is on the button.
return true;
Returns true to tell p5.js that this touch event has been handled by the button callback (spawnButton.touchStarted). This prevents the canvas tap handler from also firing.
foods.push(new Food(touchX, touchY));
If the touch was NOT on the button, create a new Food object at that location and add it to the foods array.
return false;
Returns false at the end to prevent default browser behaviors like scrolling or zooming on canvas taps.

spawnNewEater()

spawnNewEater() is a callback function that gets called whenever the user taps the spawn button. Its job is simple: instantiate a new Eater and add it to the array. The Eater constructor handles all the complex initialization, so this function can stay lightweight. This is a clean separation of concerns: the button click logic is in touchStarted(), but the spawn logic is decoupled into its own function.

function spawnNewEater() {
  eaters.push(new Eater()); // Add a new eater to the array
  // If you want to log it to the console:
  // console.log("New eater spawned! Total eaters: " + eaters.length);
}
Line-by-line explanation (1 lines)
eaters.push(new Eater());
Creates a new Eater object (which automatically picks a random starting position) and adds it to the global eaters array. From the next frame onward, draw() will update and display this new eater.

windowResized()

windowResized() is a p5.js event handler that fires automatically when the user resizes their browser window or rotates their mobile device. By calling resizeCanvas(), we ensure the sketch always fills the available space. Without this, the canvas would stay at its original size and not adapt to orientation changes.

function windowResized() {
  resizeCanvas(windowWidth, windowHeight); // Resize the canvas to match
}
Line-by-line explanation (1 lines)
resizeCanvas(windowWidth, windowHeight);
Resizes the p5.js canvas to match the new window dimensions. windowWidth and windowHeight are p5.js global variables that automatically update when the window or device orientation changes.

Food.display()

Food.display() is called every frame from draw(). The visual feedback of the orange ring is crucial for the user to understand resource claiming—when food turns orange, the user knows an eater has targeted it. This is a simple but powerful example of using visual encoding (color change) to communicate internal state. The semi-transparent orange (alpha = 150) is intentional: it lets the red beneath show through, reinforcing that this is the SAME piece of food, just with a new status.

🔬 This section draws the base food in red. What if you removed the noStroke() line and added stroke(0) before the ellipse? How would the food look different?

      fill(this.color);
      noStroke();
      ellipse(this.pos.x, this.pos.y, this.size);
  display() {
    if (!this.eaten) { // Only display if not eaten
      fill(this.color);
      noStroke();
      ellipse(this.pos.x, this.pos.y, this.size); // Draw food as an ellipse
      
      // --- Orange Indicator for Claimed Food ---
      if (this.claimedBy) {
        fill(255, 165, 0, 150); // Semi-transparent orange
        ellipse(this.pos.x, this.pos.y, this.size * 0.75); // Draw a slightly smaller orange circle
      }
    }
  }
Line-by-line explanation (7 lines)

🔧 Subcomponents:

conditional Eaten guard clause if (!this.eaten) {

Prevents drawing food that has already been eaten (though display() should not be called on eaten food anyway)

conditional Claimed food orange ring if (this.claimedBy) {

Draws a semi-transparent orange circle on top of the food to show it has been claimed by an eater

if (!this.eaten) {
Checks if the food is NOT eaten. The ! means NOT, so this block runs only when eaten is false.
fill(this.color);
Sets the fill color to this food's color (red). All shapes drawn after this will use this color.
noStroke();
Disables the outline/border on shapes. If you don't call this, ellipse() will draw a black outline by default.
ellipse(this.pos.x, this.pos.y, this.size);
Draws a red circle at the food's position. The three arguments are x, y, and diameter. this.pos is a p5.Vector, so this.pos.x and this.pos.y extract the coordinates.
if (this.claimedBy) {
Checks if this food has been claimed by an eater (claimedBy points to an Eater object). If true, we draw the orange indicator.
fill(255, 165, 0, 150);
Sets the fill color to orange (RGB 255, 165, 0) with alpha transparency of 150 (0–255 range, where 255 is fully opaque). This makes the orange ring slightly see-through.
ellipse(this.pos.x, this.pos.y, this.size * 0.75);
Draws the orange indicator circle at the same position, but with a diameter 75% the size of the original food. This creates a ring effect layered on top of the red food.

Eater.display()

Eater.display() is called every frame to render each eater's visual representation. The two key design choices here are (1) the red color change for 'leaving' eaters, which gives immediate visual feedback of state, and (2) the orange line to target food, which makes the resource-claiming relationship visible. The use of push(), translate(), and rotate() is a powerful pattern for drawing objects at arbitrary positions and angles—essential for animation. By understanding this code, you will master how to draw game characters and moving objects in p5.js.

  display() {
    let displayColor = this.color; // Start with the default color

    // If the eater is in the 'leaving' state, change its display color to red
    if (this.status === 'leaving') {
      displayColor = color(255, 0, 0); // Red color for leaving eaters
    }

    fill(displayColor); // Use the determined color for this frame
    noStroke();
    // Draw the eater as a triangle, pointing in the direction of its velocity
    // See: https://p5js.org/reference/p5/push/ and https://p5js.org/reference/p5/rotate/
    push();
    translate(this.pos.x, this.pos.y);
    rotate(this.vel.heading() + HALF_PI); // Rotate to align with velocity
    triangle(-this.size / 2, this.size / 2, this.size / 2, this.size / 2, 0, -this.size / 2);
    pop();

    // --- Draw an Orange Line to Target Food ---
    if (this.targetFood && !this.targetFood.isEaten() && this.targetFood.claimedBy === this) {
      stroke(255, 165, 0); // Orange color for the line
      strokeWeight(1); // Thin line
      line(this.pos.x, this.pos.y, this.targetFood.pos.x, this.targetFood.pos.y);
    }
  }
Line-by-line explanation (14 lines)

🔧 Subcomponents:

conditional Leaving state color change if (this.status === 'leaving') { displayColor = color(255, 0, 0); }

Changes the eater's display color from blue to red when it has eaten and is exiting the canvas

transform Rotated triangle rendering push(); translate(this.pos.x, this.pos.y); rotate(this.vel.heading() + HALF_PI); triangle(...); pop();

Moves the coordinate system to the eater's position, rotates it to point in the direction of motion, and draws a triangle within that rotated space

conditional Orange target line if (this.targetFood && !this.targetFood.isEaten() && this.targetFood.claimedBy === this) { stroke(255, 165, 0); strokeWeight(1); line(this.pos.x, this.pos.y, this.targetFood.pos.x, this.targetFood.pos.y); }

Draws an orange line from the eater to its claimed food target, providing visual feedback of the relationship

let displayColor = this.color;
Creates a local variable that starts with the eater's default color (blue). We use a separate variable so we can change the color for 'leaving' eaters without modifying the eater's actual color property.
if (this.status === 'leaving') {
Checks if the eater's status is 'leaving' (has eaten food and is exiting the canvas).
displayColor = color(255, 0, 0);
If the eater is leaving, change displayColor to red. This is a visual signal to the user that the eater is done and exiting.
fill(displayColor);
Sets the fill color for drawing. All shapes drawn after this will use displayColor (either blue or red depending on status).
noStroke();
Disables outlines on shapes, so the triangle will be solid filled without a border.
push();
Saves the current coordinate system and drawing settings. This allows us to make transformations (translate, rotate) without affecting other drawings.
translate(this.pos.x, this.pos.y);
Moves the origin (0,0) to the eater's position. All subsequent coordinates are now relative to this new origin.
rotate(this.vel.heading() + HALF_PI);
Rotates the coordinate system. this.vel.heading() returns the angle of the velocity vector (where the eater is moving). HALF_PI (90 degrees) is added because the triangle's base is at the bottom, so we rotate it 90 degrees to point in the velocity direction.
triangle(-this.size / 2, this.size / 2, this.size / 2, this.size / 2, 0, -this.size / 2);
Draws a triangle with three vertices. The two bottom vertices are at (-size/2, size/2) and (size/2, size/2), and the top vertex is at (0, -size/2). In the rotated coordinate system, this triangle points upward, which aligns with the velocity direction.
pop();
Restores the coordinate system and drawing settings that were saved by push(). After this, we're back to normal drawing in the original coordinate space.
if (this.targetFood && !this.targetFood.isEaten() && this.targetFood.claimedBy === this) {
Checks three conditions: the eater has a target food, the food is not eaten, and the food is claimed BY THIS EATER (not someone else). All three must be true to draw the line.
stroke(255, 165, 0);
Sets the stroke (line) color to orange.
strokeWeight(1);
Sets the line thickness to 1 pixel, creating a thin, subtle line.
line(this.pos.x, this.pos.y, this.targetFood.pos.x, this.targetFood.pos.y);
Draws a line from the eater's current position to the target food's position. This creates a visual connection showing the eater's intent.

Eater.update()

update() is the heart of the Eater class—it runs every frame and orchestrates all the eater's behavior. The state machine (switch statement) is a classic pattern in game and animation code: each state (seeking, wandering, eating, leaving) has its own logic, and transitions happen cleanly. The food re-evaluation logic at the top is crucial for preventing 'leaving' eaters from turning back to chase food—it's a safeguard to ensure red eaters commit to their exit. The physics integration at the bottom (vel.add(acc), vel.limit(maxSpeed), pos.add(vel), acc.mult(0)) is the Verlet-inspired physics loop that makes movement smooth and natural.

  update(foods) {
    // --- Food Re-evaluation and Wandering Logic ---
    // Only re-evaluate food if the eater is wandering or leaving,
    // OR if its current target food has become invalid (eaten or claimed by someone else).
    // An eater that is already 'seeking' and has a valid, claimed target will NOT re-evaluate.
    // This ensures red eaters (status 'leaving') will NOT turn back.
    if (this.status === 'wandering' || this.status === 'leaving' ||
        (!this.targetFood || this.targetFood.isEaten() || (this.targetFood.isClaimed() && this.targetFood.claimedBy !== this))) {

      // If the previous target food was claimed by this eater but is now invalid, release it
      if (this.targetFood && this.targetFood.claimedBy === this &&
          (this.targetFood.isEaten() || (this.targetFood.isClaimed() && this.targetFood.claimedBy !== this))) {
        this.targetFood.claimedBy = null;
      }

      let availableFood = this.findFood(foods); // This also claims the food
      if (availableFood) {
        this.targetFood = availableFood;
        this.status = 'seeking';
        this.maxSpeed = this.initialMaxSpeed; // Revert speed to initial seeking speed (normal walk)
        this.vel.set(0, 0); // Clear velocity to ensure a clean turn towards food
        this.acc.set(0, 0); // Clear acceleration
      } else {
        // If no food is available, explicitly switch to wandering
        this.targetFood = null; // Ensure no invalid target is held
        this.status = 'wandering';
        this.maxSpeed = this.initialMaxSpeed; // Wander at normal speed
      }
    }

    // State machine for eater behavior
    switch (this.status) {
      case 'seeking':
        // If a food target exists (guaranteed by global re-evaluation or fallback to wandering)
        if (this.targetFood) {
          this.seek(this.targetFood.pos); // Move towards the target food, slowing down as it gets close
          // Check if eater has reached the food
          if (this.pos.dist(this.targetFood.pos) < this.targetFood.size / 2 + this.size / 4) {
            this.status = 'eating'; // Change status to eating
            this.eatingTimer = this.eatingDuration; // Start eating timer
          }
        } else {
          // This case should ideally not be reached if global re-evaluation works,
          // but good to have a fallback to prevent being stuck.
          this.wander();
        }
        break;
      case 'wandering':
        this.wander(); // Explicitly wander
        break;
      case 'eating':
        this.eatingTimer--; // Decrement eating timer
        if (this.eatingTimer <= 0) {
          this.eat(); // If timer runs out, consume the food
        }
        break;
      case 'leaving':
        // If just entered 'leaving' state, pick a random edge to exit towards
        if (this.vel.mag() === 0) {
          let edgeTarget;
          let r = random();
          if (r < 0.25) edgeTarget = createVector(random(width), -this.size); // Top edge
          else if (r < 0.5) edgeTarget = createVector(random(width), height + this.size); // Bottom edge
          else if (r < 0.75) edgeTarget = createVector(-this.size, random(height)); // Left edge
          else edgeTarget = createVector(width + this.size, random(height)); // Right edge
          this.seek(edgeTarget); // Move towards the chosen edge
          this.maxSpeed = random(80, 100); // Speed up when leaving for a quick exit (INCREASED to be faster than normal walk)
        }
        // Red eaters (status 'leaving') will NOT re-evaluate food due to change above.
        // They will commit to their exit path and be removed by the draw loop when off-screen.
        break;
    }

    // Apply physics updates
    this.vel.add(this.acc); // Add acceleration to velocity
    this.vel.limit(this.maxSpeed); // Limit velocity to maxSpeed (which might be reduced by arrival)
    this.pos.add(this.vel); // Add velocity to position
    this.acc.mult(0); // Reset acceleration for the next frame
  }
Line-by-line explanation (34 lines)

🔧 Subcomponents:

conditional Food target re-evaluation logic if (this.status === 'wandering' || this.status === 'leaving' || (!this.targetFood || this.targetFood.isEaten() || (this.targetFood.isClaimed() && this.targetFood.claimedBy !== this))) {

Determines whether the eater should search for new food or stick with its current target

conditional Claim release on invalid target if (this.targetFood && this.targetFood.claimedBy === this && (this.targetFood.isEaten() || (this.targetFood.isClaimed() && this.targetFood.claimedBy !== this))) { this.targetFood.claimedBy = null; }

Releases the eater's claim on food if it has become invalid

switch-case Eater behavior state machine switch (this.status) {

Routes the eater's behavior based on its current state (seeking, wandering, eating, leaving)

calculation Physics velocity and position update this.vel.add(this.acc); this.vel.limit(this.maxSpeed); this.pos.add(this.vel); this.acc.mult(0);

Applies acceleration to velocity, limits velocity, updates position, and resets acceleration for the next frame

if (this.status === 'wandering' || this.status === 'leaving' ||
Begins a condition that checks if the eater should re-evaluate its food target. This runs if the eater is wandering OR leaving (states where it should not be locked to a target).
(!this.targetFood || this.targetFood.isEaten() || (this.targetFood.isClaimed() && this.targetFood.claimedBy !== this))) {
OR if the eater has no target food, OR if its target has been eaten, OR if its target was claimed by another eater. Any of these conditions triggers re-evaluation.
if (this.targetFood && this.targetFood.claimedBy === this &&
Nested check: if the eater has a target food AND the food is claimed BY THIS EATER AND the food is now invalid, release the claim.
this.targetFood.claimedBy = null;
Releases the claim so another eater can target this food.
let availableFood = this.findFood(foods);
Calls findFood(), which searches the foods array for the closest unclaimed food and claims it. Returns the food object or null if none available.
if (availableFood) {
If food was found and claimed, update the eater's target and state.
this.targetFood = availableFood;
Sets the new target food.
this.status = 'seeking';
Changes state to 'seeking' so the eater will move toward the food.
this.vel.set(0, 0);
Clears velocity to force a clean turn toward the new target (prevents momentum from previous direction).
this.acc.set(0, 0);
Clears acceleration as well for a fresh start.
} else {
If no food is available, switch to wandering.
this.status = 'wandering';
Changes state to 'wandering' so the eater will move randomly until food appears.
switch (this.status) {
Begins a switch statement that routes behavior based on the eater's current state.
case 'seeking':
If status is 'seeking', execute this block: seek the target food and check for arrival.
this.seek(this.targetFood.pos);
Calls the seek() function, which applies a steering force toward the target position using arrival behavior (slowing down as it approaches).
if (this.pos.dist(this.targetFood.pos) < this.targetFood.size / 2 + this.size / 4) {
Checks if the eater has reached the food by comparing the distance between them to a threshold (half the food's size plus a quarter of the eater's size). If distance is less than this, the eater has collided with the food.
this.status = 'eating';
Changes status to 'eating', triggering the eating animation.
this.eatingTimer = this.eatingDuration;
Sets the eating timer to 60 frames (default), so the eater will spend 60 frames eating before moving on.
case 'wandering':
If status is 'wandering', execute this block: wander randomly.
this.wander();
Calls the wander() function, which picks a random direction and moves that way.
case 'eating':
If status is 'eating', execute this block: countdown the eating timer.
this.eatingTimer--;
Decrements the eating timer by 1 each frame.
if (this.eatingTimer <= 0) {
When the timer runs out (reaches 0 or below), the eating is complete.
this.eat();
Calls the eat() function, which marks the food as eaten, clears the target, and changes status to 'leaving'.
case 'leaving':
If status is 'leaving', execute this block: move toward a random canvas edge to exit.
if (this.vel.mag() === 0) {
Checks if velocity is zero (eater just switched to 'leaving' state). This block runs only once per 'leaving' transition to pick the exit direction.
let r = random();
Generates a random number between 0 and 1, used to pick which edge to exit from.
if (r < 0.25) edgeTarget = createVector(random(width), -this.size);
25% chance: pick a random x-position along the top edge (y = -size, which is off-screen above).
this.seek(edgeTarget);
Uses steering to move toward the chosen edge, causing a natural curved exit path.
this.maxSpeed = random(80, 100);
Speeds up the eater for a quick exit when leaving (faster than normal walking speed of 70-80).
this.vel.add(this.acc);
Applies acceleration to velocity. this.acc was set by seek() or wander(), so velocity changes direction and magnitude according to the steering forces.
this.vel.limit(this.maxSpeed);
Ensures velocity never exceeds maxSpeed. This is the speed limiter in the physics simulation.
this.pos.add(this.vel);
Updates position by adding velocity. This moves the eater forward by however much velocity says it should move.
this.acc.mult(0);
Resets acceleration to zero. Next frame, steering forces will be recalculated, so we start fresh each frame.

Eater.seek()

seek() implements steering behavior, a foundational concept in game AI and animation. The key insight is that movement is governed by velocity, and velocity is changed by acceleration (steering). By calculating the desired velocity (direction + speed) and the difference between desired and current velocity, we create a steering force. The arrival behavior (slowing down near the target) is what makes movement look organic—the eater doesn't slam into food; it glides to a gentle stop. This pattern is used in professional game development and is known as 'steering behaviors' or 'boids' (see Craig Reynolds' classic work).

  seek(target) {
    let desired = p5.Vector.sub(target, this.pos); // Vector pointing from eater to target
    let d = desired.mag(); // Distance to the target

    // Arrival behavior: slow down when within slowingRadius
    if (d < this.slowingRadius) {
      // Map distance to a speed: from 0 (at target) to initialMaxSpeed (at slowingRadius)
      let m = map(d, 0, this.slowingRadius, 0, this.initialMaxSpeed); 
      desired.setMag(m); // Set magnitude to the new, slower speed
    } else {
      desired.setMag(this.initialMaxSpeed); // Otherwise, move at full speed (normal walk)
    }

    let steering = p5.Vector.sub(desired, this.vel); // Steering force = desired - current velocity
    steering.limit(this.maxForce); // Limit steering force
    this.applyForce(steering); // Apply the steering force
  }
Line-by-line explanation (10 lines)

🔧 Subcomponents:

calculation Direction vector to target let desired = p5.Vector.sub(target, this.pos);

Calculates the vector from the eater to the target

conditional Arrival damping logic if (d < this.slowingRadius) { let m = map(d, 0, this.slowingRadius, 0, this.initialMaxSpeed); desired.setMag(m); } else { desired.setMag(this.initialMaxSpeed); }

Implements smooth deceleration as the eater approaches the target

calculation Steering force calculation let steering = p5.Vector.sub(desired, this.vel); steering.limit(this.maxForce);

Computes the steering force needed to match desired velocity and limits it for smooth motion

let desired = p5.Vector.sub(target, this.pos);
Uses p5.Vector.sub() to subtract the eater's position from the target position. The result is a vector pointing FROM the eater TO the target.
let d = desired.mag();
Gets the magnitude (length) of the desired vector, which is the straight-line distance from the eater to the target.
if (d < this.slowingRadius) {
Checks if the eater is within the slowingRadius (default 20 pixels). If so, the eater should start slowing down as it approaches.
let m = map(d, 0, this.slowingRadius, 0, this.initialMaxSpeed);
Uses the map() function to convert distance d into a speed m. At distance 0 (arrived), m = 0. At distance slowingRadius (far edge of slowing zone), m = initialMaxSpeed. In between, speed is proportional to distance—the closer you are, the slower you go.
desired.setMag(m);
Sets the magnitude of the desired vector to m, reducing its length. This makes the eater move more slowly toward the target as it gets close.
} else {
If the eater is farther than slowingRadius, keep moving at full speed.
desired.setMag(this.initialMaxSpeed);
Sets desired to full speed in the direction of the target. The eater will try to move at initialMaxSpeed (70-80 pixels/frame) toward the target.
let steering = p5.Vector.sub(desired, this.vel);
Calculates steering as the difference between where the eater WANTS to go (desired) and where it's CURRENTLY going (vel). If they match, steering is zero and no change is needed. If they differ, steering is the force needed to adjust velocity toward desired.
steering.limit(this.maxForce);
Limits the steering force to maxForce (0.5 by default). This prevents unrealistically sharp turns; the eater can't change direction infinitely fast.
this.applyForce(steering);
Applies the steering force to the eater's acceleration, which will be integrated into velocity in update().

Eater.wander()

wander() is a steering behavior that creates natural-looking random movement. Instead of picking a random direction each frame (which would cause jittery, erratic motion), it picks a random point on a circle ahead of the eater and steers toward it. The lookahead component (vel * 50) makes wandering biased in the direction the eater is already moving, so it doesn't suddenly reverse course. Combined with seek(), this creates smooth, organic wandering patterns.

🔬 The wander target is projected 50 timesteps ahead of the eater. What happens if you change 50 to a much smaller number like 10, or a larger number like 100? How would the wandering look different?

    let wanderTarget = this.pos.copy();
    wanderTarget.add(p5.Vector.mult(this.vel, 50));
  wander() {
    let wanderTarget = this.pos.copy();
    wanderTarget.add(p5.Vector.mult(this.vel, 50)); // Look a bit ahead in current direction
    let wanderRadius = 20; // Radius of the wander circle
    let wanderAngle = random(TWO_PI); // Random angle for the wander
    wanderTarget.x += wanderRadius * cos(wanderAngle);
    wanderTarget.y += wanderRadius * sin(wanderAngle);
    this.seek(wanderTarget); // Seek this random wander target
  }
Line-by-line explanation (7 lines)

🔧 Subcomponents:

calculation Lookahead calculation wanderTarget.add(p5.Vector.mult(this.vel, 50));

Projects a point ahead of the eater based on its current velocity, so wandering feels forward-looking

calculation Random angle offset let wanderAngle = random(TWO_PI); wanderTarget.x += wanderRadius * cos(wanderAngle); wanderTarget.y += wanderRadius * sin(wanderAngle);

Adds a random offset around a circle to create unpredictable wandering direction

let wanderTarget = this.pos.copy();
Creates a copy of the eater's current position. We use copy() so we don't modify the original this.pos.
wanderTarget.add(p5.Vector.mult(this.vel, 50));
Multiplies the velocity by 50 and adds it to wanderTarget. This pushes the target point 50 'timesteps' ahead in the current direction of movement, creating a lookahead point. This makes wandering feel forward-looking rather than random flailing.
let wanderRadius = 20;
Defines the radius of a circle around the lookahead point from which a random target will be picked.
let wanderAngle = random(TWO_PI);
Picks a random angle between 0 and 2π (0 to 360 degrees). TWO_PI is a p5.js constant.
wanderTarget.x += wanderRadius * cos(wanderAngle);
Uses cos() to convert the angle into an x-offset. This adds a horizontal displacement to the wander target.
wanderTarget.y += wanderRadius * sin(wanderAngle);
Uses sin() to convert the angle into a y-offset. This adds a vertical displacement to the wander target.
this.seek(wanderTarget);
Calls seek() with the wander target. The eater will steer toward this random point as if it were seeking food.

Eater.applyForce()

applyForce() is a simple helper method that accumulates steering forces. It's part of the Newtonian physics integration pattern: force → acceleration → velocity → position. By centralizing force application here, the code stays clean and modular. Seek and wander both call this method, so all steering forces are funneled through one place.

  applyForce(force) {
    this.acc.add(force);
  }
Line-by-line explanation (1 lines)
this.acc.add(force);
Adds the force vector to the eater's acceleration. In the next update(), this acceleration will be added to velocity, which changes how the eater moves.

Eater.findFood()

findFood() implements a greedy nearest-neighbor search for unclaimed resources. It loops through all food and tracks the closest one, preventing competition by claiming food as soon as it's found. The key design is the condition that allows re-claiming food already claimed by THIS eater—this ensures an eater won't lose its target to another eater's findFood() call. This is the resource-claiming system's core: claim early, claim often, but only one eater per food.

🔬 The condition allows an eater to retarget food it has already claimed. What happens if you change || food.claimedBy === this to just remove it entirely? Would eaters get confused and swap targets?

    for (let food of foods) {
      // Only consider food that is not eaten AND not claimed by another eater
      if (!food.isEaten() && (!food.isClaimed() || food.claimedBy === this)) {
  findFood(foods) {
    let closestFood = null;
    let closestDist = Infinity;
    for (let food of foods) {
      // Only consider food that is not eaten AND not claimed by another eater
      if (!food.isEaten() && (!food.isClaimed() || food.claimedBy === this)) {
        let d = this.pos.dist(food.pos);
        if (d < closestDist) {
          closestDist = d;
          closestFood = food;
        }
      }
    }

    // If food is found, claim it
    if (closestFood) {
      closestFood.claimedBy = this;
    }
    return closestFood;
  }
Line-by-line explanation (11 lines)

🔧 Subcomponents:

conditional Valid food check if (!food.isEaten() && (!food.isClaimed() || food.claimedBy === this)) {

Ensures we only consider unclaimed food or food already claimed by this eater

conditional Closest food tracking if (d < closestDist) {

Updates the closest food reference when a nearer food is found

calculation Food claiming if (closestFood) { closestFood.claimedBy = this; }

Marks the closest food as claimed by this eater

let closestFood = null;
Initialize a variable to store the closest food found. Starts as null (no food yet).
let closestDist = Infinity;
Initialize the closest distance to Infinity. This ensures the first food encountered will be closer than Infinity and will update closestFood.
for (let food of foods) {
Loops through all food objects in the foods array.
if (!food.isEaten() && (!food.isClaimed() || food.claimedBy === this)) {
Checks two conditions: (1) the food is not eaten, AND (2) either the food is not claimed by anyone, OR the food is already claimed by THIS eater. This lets the eater stick with its current target if it was already targeting this food, but also find new food if the current target was claimed by someone else.
let d = this.pos.dist(food.pos);
Calculates the distance from the eater to this food using the dist() method.
if (d < closestDist) {
Checks if this food is closer than the previously closest food. If so, update the closest food reference.
closestDist = d;
Updates closestDist to this food's distance.
closestFood = food;
Updates closestFood to point to this food.
if (closestFood) {
After the loop, if a closest food was found (not null), claim it.
closestFood.claimedBy = this;
Sets the food's claimedBy reference to this eater, marking it as claimed.
return closestFood;
Returns the closest food found (or null if no valid food exists). The caller uses this to update their target.

Eater.eat()

eat() is the final step in the eater's lifecycle. It's called when the eating timer runs out (see update()). The validation check is important—if another eater had stolen the food after this eater sat down to eat, the check prevents errors. Once food is eaten, the eater leaves the canvas, creating a natural loop: seek → eat → leave → (respawn via button).

  eat() {
    if (this.targetFood && !this.targetFood.isEaten() && this.targetFood.claimedBy === this) {
      this.targetFood.markEaten(); // Mark the food as eaten (and releases claim)
      this.targetFood = null; // Clear the target food
      this.status = 'leaving'; // Change status to leaving
      this.maxSpeed = this.initialMaxSpeed; // Reset speed for leaving (normal walk for next seeking)
    }
  }
Line-by-line explanation (5 lines)

🔧 Subcomponents:

conditional Food consumption validation if (this.targetFood && !this.targetFood.isEaten() && this.targetFood.claimedBy === this) {

Ensures the eater can only eat its claimed, uneaten target food

if (this.targetFood && !this.targetFood.isEaten() && this.targetFood.claimedBy === this) {
Triple-checks that: (1) the eater has a target food, (2) it hasn't been eaten yet, and (3) it's claimed BY THIS EATER. If all three are true, proceed with eating.
this.targetFood.markEaten();
Calls the food's markEaten() method, which sets the food's eaten flag to true and releases the claim.
this.targetFood = null;
Clears the eater's target food reference, so it won't try to eat the same food again.
this.status = 'leaving';
Changes the eater's status to 'leaving', triggering the exit behavior in the state machine.
this.maxSpeed = this.initialMaxSpeed;
Resets maxSpeed to the initial seeking speed (70-80). When the eater begins its 'leaving' routine, it will speed up further in update(). This reset ensures clean state transitions.

Eater.isOffScreen()

isOffScreen() is a simple boundary check used by draw() to remove eaters that have fully exited the canvas. Checking against -size and width+size (rather than just 0 and width) ensures the entire eater is off-screen before removal, preventing visual popping.

  isOffScreen() {
    return (
      this.pos.x < -this.size ||
      this.pos.x > width + this.size ||
      this.pos.y < -this.size ||
      this.pos.y > height + this.size
    );
  }
Line-by-line explanation (4 lines)
this.pos.x < -this.size ||
Checks if the eater's left edge (pos.x - size/2, approximately) has moved past the left edge of the canvas. Returns true if the eater has exited left.
this.pos.x > width + this.size ||
Checks if the eater's right edge has moved past the right edge. Returns true if the eater has exited right.
this.pos.y < -this.size ||
Checks if the eater's top edge has moved past the top. Returns true if the eater has exited up.
this.pos.y > height + this.size
Checks if the eater's bottom edge has moved past the bottom. Returns true if the eater has exited down.

📦 Key Variables

foods array

Global array storing all active Food objects. Food items are added when the user taps the canvas and removed when eaten or claimed.

let foods = [];
eaters array

Global array storing all active Eater objects. Eaters are added at setup and when the spawn button is tapped, and removed when they exit the canvas.

let eaters = [];
EATER_COUNT number

Constant defining how many eaters are spawned when the sketch starts. Can be changed to adjust initial population.

const EATER_COUNT = 5;
spawnButton object (p5.Renderer)

Reference to the HTML button element with id='spawnButton'. Used to attach the touchStarted event listener.

spawnButton = select('#spawnButton');
pos p5.Vector

Stores the eater or food's position on the canvas as a vector with x and y components.

this.pos = createVector(100, 150);
vel p5.Vector

Stores the eater's velocity (how fast and in what direction it's moving).

this.vel = createVector();
acc p5.Vector

Stores the eater's acceleration (how much velocity changes each frame due to steering forces).

this.acc = createVector();
maxSpeed number

The eater's maximum speed limit in pixels per frame. Varies based on state (seeking, leaving, wandering).

this.maxSpeed = random(70, 80);
initialMaxSpeed number

Stores the original maxSpeed value so it can be restored when switching states.

this.initialMaxSpeed = this.maxSpeed;
status string

The eater's current behavioral state: 'seeking', 'eating', 'leaving', or 'wandering'. Controls which behavior executes in the state machine.

this.status = 'seeking';
targetFood Food object or null

Reference to the specific Food object this eater is currently targeting. Null when no target.

this.targetFood = availableFood;
eatingTimer number

Countdown timer for the eating animation. Decrements each frame during 'eating' state.

this.eatingTimer = this.eatingDuration;
eatingDuration number

How many frames an eater spends eating before the food is marked eaten. Default 60.

this.eatingDuration = 60;
claimedBy Eater object or null

Reference to the Eater that has claimed this food. Null if unclaimed.

this.claimedBy = null;
eaten boolean

Flag indicating whether this food has been consumed. True means eaten, false means available.

this.eaten = false;
color (Food) p5.Color

The color of the food (red). Set at creation and used in display().

this.color = color(255, 100, 100);
color (Eater) p5.Color

The eater's default color (blue). Changed to red in display() when status is 'leaving'.

this.color = color(50, 150, 200);
size number

The eater or food's size in pixels (diameter for food, base dimension for triangular eater).

this.size = random(10, 20);
slowingRadius number

Distance from target within which the eater begins to slow down (arrival behavior). Default 20 pixels.

this.slowingRadius = 20;
maxForce number

Maximum steering force applied per frame. Limits the eater's ability to turn sharply. Default 0.5.

this.maxForce = 0.5;

🔧 Potential Improvements (5)

Here are some ways this code could be enhanced:

PERFORMANCE Eater.findFood()

Loops through ALL food items every re-evaluation frame, which grows expensive as food count increases. For a sketch with 100+ food items, this becomes a bottleneck.

💡 Implement spatial partitioning (grid-based or quadtree) to only search nearby food. Or cache the closest food and only re-search if the eater moves a certain distance.

BUG touchStarted() button hit detection

Assumes the canvas is positioned at (0,0) in the viewport and uses buttonRect directly against touches[0]. On some devices or layouts (especially with CSS transforms or padding), this coordinate mapping could fail.

💡 Account for canvas offset: const canvasOffset = spawnButton.elt.getBoundingClientRect(); then adjust touch coordinates relative to canvas position using canvas offsets.

STYLE Eater.update() state machine

The 're-evaluation' conditional at the top is long and complex; the logic could be clearer.

💡 Extract a helper method shouldReevaluateFood() that returns true if re-evaluation is needed. This improves readability and makes the state machine logic easier to follow.

FEATURE Eater.eat()

When an eater finishes eating, it simply leaves. There's no visual feedback (e.g., a 'satisfied' animation) or sound.

💡 Add a brief 'celebration' animation: scale the eater up briefly, or flash yellow before turning red and leaving. This makes eating feel rewarding and gives users clearer feedback.

BUG Eater.update() 'leaving' state

If vel.mag() === 0 when entering 'leaving', a target edge is picked. But if an eater is already moving and somehow switches to 'leaving', it won't recalculate the edge target.

💡 Use a flag like hasPickedExitEdge to ensure edge selection happens exactly once, or always recalculate the edge on state transition in update().

🔄 Code Flow

Code flow showing setup, draw, touchstarted, spawnnweater, windowresized, fooddisplay, eaterdisplay, eaterupdate, seek, wander, applyforce, findfood, eat, isoffscreen

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

graph TD start[Start] --> setup[setup] setup --> canvas-creation[Canvas Creation] setup --> eater-spawn-loop[Eater Spawn Loop] setup --> button-selection[Button Selection] setup --> button-event-attach[Button Event Attach] setup --> windowresized[windowResized] setup --> draw[draw loop] click setup href "#fn-setup" click canvas-creation href "#sub-canvas-creation" click eater-spawn-loop href "#sub-eater-spawn-loop" click button-selection href "#sub-button-selection" click button-event-attach href "#sub-button-event-attach" click windowresized href "#fn-windowresized" click draw href "#fn-draw" draw --> food-loop[Food Loop] draw --> eater-loop[Eater Loop] draw --> isoffscreen[Off-Screen Check] click food-loop href "#sub-food-loop" click eater-loop href "#sub-eater-loop" click isoffscreen href "#fn-isoffscreen" food-loop --> eaten-check[Eaten Check] food-loop --> orange-claimed-indicator[Orange Claimed Indicator] click eaten-check href "#sub-eaten-check" click orange-claimed-indicator href "#sub-orange-claimed-indicator" eater-loop --> eaterupdate[Eater Update] eater-loop --> eaterdisplay[Eater Display] click eaterupdate href "#fn-eaterupdate" click eaterdisplay href "#fn-eaterdisplay" eaterupdate --> state-machine[State Machine] eaterupdate --> food-reevaluation[Food Reevaluation] eaterupdate --> physics-integration[Physics Integration] click state-machine href "#sub-state-machine" click food-reevaluation href "#sub-food-reevaluation" click physics-integration href "#sub-physics-integration" state-machine --> seek[Seek] state-machine --> wander[Wander] state-machine --> eat[eat] state-machine --> eater-exit-check[Eater Exit Check] click seek href "#fn-seek" click wander href "#fn-wander" click eat href "#fn-eat" click eater-exit-check href "#sub-eater-exit-check" seek --> desired-vector[Desired Vector] seek --> arrival-behavior[Arrival Behavior] seek --> steering-force[Steering Force] click desired-vector href "#sub-desired-vector" click arrival-behavior href "#sub-arrival-behavior" click steering-force href "#sub-steering-force" wander --> lookahead[Lookahead] wander --> random-offset[Random Offset] click lookahead href "#sub-lookahead" click random-offset href "#sub-random-offset" food-reevaluation --> food-filter[Food Filter] food-reevaluation --> distance-check[Distance Check] food-reevaluation --> claim-assignment[Claim Assignment] click food-filter href "#sub-food-filter" click distance-check href "#sub-distance-check" click claim-assignment href "#sub-claim-assignment" eat --> eat-validation[Eat Validation] click eat-validation href "#sub-eat-validation"

Preview

It’s fix let’s go but I’m just fixing orange target - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of It’s fix let’s go but I’m just fixing orange target - Code flow showing setup, draw, touchstarted, spawnnweater, windowresized, fooddisplay, eaterdisplay, eaterupdate, seek, wander, applyforce, findfood, eat, isoffscreen
Code Flow Diagram