This sketch simulates a busy emoji restaurant where customers arrive, find seats at tables, eat food, and leave. Click or tap the kitchen area to generate food items that customers will eat, creating a dynamic, interactive restaurant simulation with state machines and object-oriented design.
This sketch creates a lively emoji restaurant where small colored circles (customers) walk in, find empty chairs at four tables, eat food you generate by clicking, and leave. The magic lies in three custom classes—Customer, Table, and Food—that work together using state machines to create believable behavior. You will see customers arrive continuously, wander if all tables are full, sit down and animate eating, then depart. Each customer moves through distinct states (ARRIVING, SEEKING_TABLE, MOVING_TO_CHAIR, EATING, LEAVING), and the code uses `atan2()` and distance calculations to navigate them toward their target chairs.
The sketch is organized into a setup() that creates the four tables, a draw() loop that updates and displays all objects, and three class definitions that handle the logic for customers, tables, and food. By studying it, you will learn how to structure complex simulations using classes, how state machines simplify behavior, how to use object references to link related entities (chairs to customers, food to tables), and how to clean up finished objects from arrays to keep memory efficient.
⚙️ How It Works
When the sketch loads, setup() creates a canvas, defines four table objects positioned around the screen, and spawns an initial batch of customers starting off-screen to the left.
Every frame, draw() refreshes the background, draws the kitchen and tables, and loops through all customers and food items to update and display them.
New customers arrive automatically every 180 frames if the maximum hasn't been reached; each new customer enters from the left with a random speed and skin tone.
Each customer runs an update() method that follows a state machine: ARRIVING (walk right until x ≥ 200), SEEKING_TABLE (scan tables for empty chairs and navigate toward one), MOVING_TO_CHAIR (move toward the target chair using atan2 and trigonometry), EATING (stay seated and animate eating for 180–360 frames), and LEAVING (exit to the right).
When a customer sits, the eat() method creates a new Food object with a random emoji at the chair and links it; the customer's mouth opens periodically to animate eating.
You can click or tap the kitchen to generate new food items; they appear in the kitchen area ready for customers to eat.
When a customer finishes eating, the food is marked as eaten and removed; the chair is freed and the customer exits.
🎓 Concepts You'll Learn
State machines (ARRIVING, SEEKING_TABLE, MOVING_TO_CHAIR, EATING, LEAVING)Object-oriented design with classesPathfinding with atan2 and trigonometryObject references and linkingArray cleanup and memory managementInput handling (mousePressed, touchStarted)Window responsiveness (windowResized)
📝 Code Breakdown
setup()
setup() runs once when the sketch starts. It is the perfect place to initialize the canvas, create game objects, and set up starting conditions. Notice how we use width and height (p5.js variables that automatically match the canvas size) to position tables responsively.
function setup() {
createCanvas(windowWidth, windowHeight);
textFont('Arial'); // Emojis usually work well with a default system font
// Define table layout (adjust positions as needed)
tables.push(new Table(width / 3, height / 3, 2));
tables.push(new Table(2 * width / 3, height / 3, 2));
tables.push(new Table(width / 3, 2 * height / 3, 2));
tables.push(new Table(2 * width / 3, 2 * height / 3, 2));
// Initialize initial customers
for (let i = 0; i < numInitialCustomers; i++) {
customers.push(new Customer(random(-100, -50), random(height))); // Start off-screen
}
}
Creates a new Customer at a random position off-screen left (x between -100 and -50) and a random vertical position, then adds it to the customers array
draw()
draw() is called 60 times per second by p5.js. It is where animation happens: background() clears the old frame, then display() and update() methods on all objects redraw the new frame. Notice we loop backwards through customers when removing them—this prevents skipping items when the array shrinks.
🔬 This loop counts backwards (from length-1 down to 0) instead of forwards. What do you think would break if you changed it to count forwards (let i = 0; i < customers.length; i++)? Try it and remove a customer while the loop runs—why does something disappear?
// Update and display customers
for (let i = customers.length - 1; i >= 0; i--) {
let customer = customers[i];
customer.update(); // Update customer state and position
customer.display(); // Draw the customer
🔬 The !food.inKitchen check prevents kitchen food from being removed. What happens if you remove that check and change the condition to just if (food.isEaten()) {}? Try generating food in the kitchen, letting it be eaten, then clicking the kitchen again—do you think kitchen food should stick around?
// Remove food if eaten (and not in kitchen)
if (food.isEaten() && !food.inKitchen) {
foodItems.splice(i, 1);
}
function draw() {
background(240, 230, 200); // Restaurant floor color
// Draw kitchen
fill(180);
rect(kitchenPos.x, kitchenPos.y, kitchenPos.width, kitchenPos.height);
fill(0);
textSize(18);
text("Kitchen", kitchenPos.x + kitchenPos.width / 2, kitchenPos.y + kitchenPos.height / 2);
// Draw tables and chairs
for (let table of tables) {
table.display();
}
// Update and display customers
for (let i = customers.length - 1; i >= 0; i--) {
let customer = customers[i];
customer.update(); // Update customer state and position
customer.display(); // Draw the customer
// Remove customer if they've left the screen
if (customer.state === 'LEAVING' && customer.x > width + 50) {
customers.splice(i, 1);
}
}
// Attempt to add new customers
if (frameCount - lastCustomerArrival > customerArrivalInterval && customers.length < maxCustomers) {
customers.push(new Customer(random(-100, -50), random(height)));
lastCustomerArrival = frameCount;
}
// Update and display food items (only those currently on tables or in kitchen)
for (let i = foodItems.length - 1; i >= 0; i--) {
let food = foodItems[i];
food.display();
// Remove food if eaten (and not in kitchen)
if (food.isEaten() && !food.inKitchen) {
foodItems.splice(i, 1);
}
}
}
Checks two conditions: enough frames have elapsed since the last arrival (frameCount is a p5.js variable counting frames), AND we haven't hit the max customer limit
Creates and adds a new Customer at a random off-screen position to the left and a random height
lastCustomerArrival = frameCount;
Records the current frame number so we can wait customerArrivalInterval frames before allowing another arrival
food.display();
Calls display() on the food item to draw its emoji
if (food.isEaten() && !food.inKitchen) {
Checks if the food has been eaten AND is not in the kitchen (kitchen food stays visible as a supply)
foodItems.splice(i, 1);
Removes the eaten food from the array to free memory
windowResized()
windowResized() is a special p5.js function that fires whenever the user resizes their browser window. By recreating the tables with fractional coordinates (width / 3, etc.), we make the restaurant responsive to different screen sizes.
Clears and recreates all four tables at positions proportional to the new canvas size
resizeCanvas(windowWidth, windowHeight);
p5.js automatically calls this function when the browser window is resized; it stretches the canvas to the new dimensions
tables = [];
Clears the tables array so we can recreate tables at the correct new positions (essential—otherwise old tables stay at old positions)
tables.push(new Table(width / 3, height / 3, 2));
Recreates the first table using the new width and height, which now proportionally position it in the resized canvas
mousePressed()
mousePressed() is a p5.js event handler that fires once per click. It is the main way players interact with the restaurant—you are the chef generating food. Notice the fourth parameter is true, marking food as inKitchen so it won't be garbage-collected when eaten.
// Function to generate new food on touch/click (in kitchen)
function mousePressed() {
foodItems.push(new Food(kitchenPos.x + random(kitchenPos.width), kitchenPos.y + random(kitchenPos.height), random(foodEmojis), true));
}
Line-by-line explanation (2 lines)
function mousePressed() {
p5.js automatically calls this function whenever the mouse is clicked anywhere on the canvas
Creates a new Food object with: x position = kitchen's left edge plus a random offset within its width, y position = kitchen's top plus a random offset within its height, emoji = a random one from foodEmojis, and inKitchen = true (marking it as kitchen food)
touchStarted()
touchStarted() mirrors mousePressed() to support mobile and tablet users. Returning false prevents the browser from interpreting the touch as a scroll or zoom, keeping focus on your game.
// Allow touch input to also generate food (in kitchen)
function touchStarted() {
foodItems.push(new Food(kitchenPos.x + random(kitchenPos.width), kitchenPos.y + random(kitchenPos.height), random(foodEmojis), true));
return false; // Prevent default browser touch actions (like scrolling)
}
Line-by-line explanation (3 lines)
function touchStarted() {
p5.js automatically calls this function when the user touches the screen (on mobile devices)
Identical to mousePressed()—creates a new Food object at a random position within the kitchen with a random emoji
return false;
Tells the browser NOT to perform its default touch actions (like scrolling or zooming)—essential for touch games to feel responsive
Customer (Class)
The Customer constructor runs once when you create a new Customer with new Customer(x, y). Every property defined with 'this.' becomes a variable that belongs to that specific customer and can be accessed later in other methods like update() and display().
class Customer {
constructor(x, y) {
this.x = x;
this.y = y;
this.size = random(30, 45); // Random size for variety
this.color = color(random(100, 200), random(100, 200), random(100, 200)); // Random skin tone-ish color
this.speed = random(1.5, 2.5); // Movement speed
this.state = 'ARRIVING'; // Current state: ARRIVING, SEEKING_TABLE, MOVING_TO_CHAIR, EATING, LEAVING
this.targetChair = null; // Reference to the chair this customer is aiming for
this.mouthOpen = false; // For eating animation
this.eatingTimer = 0; // Timer to keep mouth open briefly
this.eatingDuration = random(180, 360); // How long the customer eats (frames)
}
Creates a random color with RGB values each between 100 and 200, giving varied but realistic skin tones
this.speed = random(1.5, 2.5);
Picks a random movement speed between 1.5 and 2.5 pixels per frame—customers move at slightly different paces
this.state = 'ARRIVING';
Sets the initial state to ARRIVING—the first state in the customer's journey through the restaurant
this.targetChair = null;
Initializes the target chair reference to null—it will be set once the customer finds an empty chair
this.mouthOpen = false;
Starts with the mouth closed; it will open periodically during eating to animate chewing
this.eatingTimer = 0;
Initializes the eating timer to 0; it will be set to 20 whenever the mouth should open
this.eatingDuration = random(180, 360);
Picks how long this customer will eat (180–360 frames, roughly 3–6 seconds at 60 fps)—makes eating times vary
Customer.display()
display() is called every frame by the draw() loop. It uses translate() to position everything relative to the customer (via push/pop), scales shapes by this.size so bigger customers look proportionally bigger, and animates the mouth by conditionally drawing an arc vs. a line based on the eating state.
🔬 These two lines draw the left and right eyes symmetrically. What happens if you change the first -this.size * 0.15 to -this.size * 0.3 (further left)? Try it—can you make the eyes cross-eyed or give each eye a different size?
Saves the drawing state, translates to the customer's position so all shapes are drawn relative to them, then restores the state—prevents transforms from affecting other drawings
blockBody and Head Drawingrect(-this.size / 4, -this.size / 2, this.size / 2, this.size);
ellipse(0, -this.size / 2, this.size * 0.8);
Draws a simple rectangular body and circular head centered at the origin (0,0)
Draws a straight line representing a closed mouth from left to right across the face
pop();
Restores the drawing state, undoing the translate() so other objects draw at their correct positions
Customer.update()
update() is the state machine engine. It runs every frame and uses a switch statement to decide what the customer does based on this.state. Each case transitions to the next state in a chain: ARRIVING → SEEKING_TABLE → MOVING_TO_CHAIR → EATING → LEAVING. The MOVING_TO_CHAIR case demonstrates pathfinding using atan2() and trigonometric functions to move smoothly toward a target.
🔬 This loop searches ALL tables and claims the FIRST empty chair it finds. What happens if you remove the break statement? What happens if you remove foundChair = true? Try removing both and watch what happens—does the customer claim multiple chairs?
case 'SEEKING_TABLE':
let foundChair = false;
for (let table of tables) {
let chairIndex = table.findEmptyChair();
if (chairIndex !== -1) {
this.targetChair = table.chairs[chairIndex];
this.targetChair.customer = this; // Link chair to customer
this.state = 'MOVING_TO_CHAIR';
foundChair = true;
break;
update() {
switch (this.state) {
case 'ARRIVING':
this.x += this.speed;
if (this.x >= 200) { // Entrance area
this.state = 'SEEKING_TABLE';
}
break;
case 'SEEKING_TABLE':
let foundChair = false;
for (let table of tables) {
let chairIndex = table.findEmptyChair();
if (chairIndex !== -1) {
this.targetChair = table.chairs[chairIndex];
this.targetChair.customer = this; // Link chair to customer
this.state = 'MOVING_TO_CHAIR';
foundChair = true;
break;
}
}
// If no chair found, wander slightly
if (!foundChair) {
this.x += random(-1, 1) * this.speed * 0.2;
this.y += random(-1, 1) * this.speed * 0.2;
this.x = constrain(this.x, 0, width);
this.y = constrain(this.y, 0, height);
}
break;
case 'MOVING_TO_CHAIR':
if (this.targetChair) {
let dx = this.targetChair.x - this.x;
let dy = this.targetChair.y - this.y;
let angle = atan2(dy, dx);
this.x += cos(angle) * this.speed;
this.y += sin(angle) * this.speed;
// Check if customer has reached the chair
if (dist(this.x, this.y, this.targetChair.x, this.targetChair.y) < 5) {
this.x = this.targetChair.x; // Snap to chair
this.y = this.targetChair.y;
this.state = 'EATING';
this.targetChair.occupied = true; // Mark chair as occupied
this.eat(); // Start eating process
}
}
break;
case 'EATING':
this.eatingDuration--;
// Periodically open mouth while eating
if (this.eatingDuration % 60 === 0) {
this.mouthOpen = true;
this.eatingTimer = 20; // Keep mouth open for 20 frames
}
if (this.eatingDuration <= 0) {
this.state = 'LEAVING';
this.targetChair.occupied = false; // Free up the chair
this.targetChair.customer = null; // Unlink customer from chair
if (this.targetChair.food) {
this.targetChair.food.setEaten(); // Mark food as eaten
this.targetChair.food = null; // Remove food from table
}
}
break;
case 'LEAVING':
this.x += this.speed; // Move off-screen to the right
break;
}
}
Starts a switch statement that checks the current state and runs different code for each one—the heart of the state machine
case 'ARRIVING':
Handles the ARRIVING state: customer is walking in from the left
this.x += this.speed;
Moves the customer right by their speed each frame
if (this.x >= 200) {
Once the customer reaches x=200 (past the entrance), they've entered the restaurant
this.state = 'SEEKING_TABLE';
Transitions to the next state: looking for an empty chair
case 'SEEKING_TABLE':
Handles the SEEKING_TABLE state: customer is looking for an empty chair
let foundChair = false;
Flag that tracks whether we found an empty chair—starts false
for (let table of tables) {
Loops through all tables to search for an empty chair
let chairIndex = table.findEmptyChair();
Calls the table's findEmptyChair() method to get the index of an empty chair (or -1 if none)
if (chairIndex !== -1) {
If findEmptyChair() returned a valid index (not -1), there is an empty chair
this.targetChair = table.chairs[chairIndex];
Stores a reference to the chair object so the customer can move toward it
this.targetChair.customer = this;
Links the chair back to the customer—now the chair knows which customer is coming to sit
this.state = 'MOVING_TO_CHAIR';
Transitions to the next state: walking toward the claimed chair
foundChair = true;
Sets the flag to true to indicate success
break;
Exits the for loop early since we found a chair and don't need to keep searching
if (!foundChair) {
If no chair was found, the customer must wait and wander
this.x += random(-1, 1) * this.speed * 0.2;
Adds a small random movement left or right (scaled to 20% of speed) so the customer looks like they're wandering nervously
this.y += random(-1, 1) * this.speed * 0.2;
Also wanders slightly up and down
this.x = constrain(this.x, 0, width);
Clamps x between 0 and width so the wandering customer doesn't leave the screen
this.y = constrain(this.y, 0, height);
Clamps y between 0 and height for the same reason
case 'MOVING_TO_CHAIR':
Handles the MOVING_TO_CHAIR state: customer walks toward their target chair
if (this.targetChair) {
Safety check: only move if we actually have a target chair
let dx = this.targetChair.x - this.x;
Calculates the horizontal distance from customer to target chair
let dy = this.targetChair.y - this.y;
Calculates the vertical distance from customer to target chair
let angle = atan2(dy, dx);
Converts the dx and dy distances to an angle (in radians) pointing from the customer toward the chair
this.x += cos(angle) * this.speed;
Moves the customer toward the target by moving along the angle using cos() to extract the x-component
this.y += sin(angle) * this.speed;
Moves the customer by using sin() to extract the y-component of motion along the angle
if (dist(this.x, this.y, this.targetChair.x, this.targetChair.y) < 5) {
Checks if the distance between customer and chair is less than 5 pixels—close enough to sit
this.x = this.targetChair.x;
Snaps the customer's x position exactly to the chair's x
this.y = this.targetChair.y;
Snaps the customer's y position exactly to the chair's y (now perfectly seated)
this.state = 'EATING';
Transitions to the eating state
this.targetChair.occupied = true;
Marks the chair as occupied so no other customer will try to sit there
this.eat();
Calls the eat() method to generate food at the table
case 'EATING':
Handles the EATING state: customer sits and eats
this.eatingDuration--;
Decrements the eating countdown by 1 frame each call
if (this.eatingDuration % 60 === 0) {
Every 60 frames (roughly every second), the modulo operator % returns 0
this.mouthOpen = true;
Opens the mouth to animate eating
this.eatingTimer = 20;
Sets the mouth timer to 20 frames so it stays open for a brief animation
if (this.eatingDuration <= 0) {
Once eating time runs out, transition to leaving
this.state = 'LEAVING';
Changes state to LEAVING
this.targetChair.occupied = false;
Frees up the chair for the next customer
this.targetChair.customer = null;
Unlinks the customer from the chair
if (this.targetChair.food) {
Checks if there is food on the table
this.targetChair.food.setEaten();
Marks the food as eaten (but kitchen food may persist for reuse)
this.targetChair.food = null;
Removes the food reference from the chair
case 'LEAVING':
Handles the LEAVING state: customer walks off to the right
this.x += this.speed;
Moves the customer right off the canvas
Customer.eat()
eat() is called once when the customer reaches their chair. It creates the food emoji at the table and opens the mouth. Notice the y position is offset by this.size * 0.6 to place food above the customer's head rather than at the chair center.
eat() {
// Generate food directly at the customer's chair
let food = new Food(this.targetChair.x, this.targetChair.y + this.size * 0.6, random(foodEmojis), false);
foodItems.push(food);
this.targetChair.food = food; // Link food to chair
this.mouthOpen = true;
this.eatingTimer = 20;
}
Line-by-line explanation (5 lines)
let food = new Food(this.targetChair.x, this.targetChair.y + this.size * 0.6, random(foodEmojis), false);
Creates a new Food object: x = chair's x position (above the customer's head), y = chair's y plus a small offset, emoji = random from foodEmojis array, inKitchen = false (this food is on a table, not in the kitchen)
foodItems.push(food);
Adds the food to the global foodItems array so draw() will display it
this.targetChair.food = food;
Links the food object to the chair so we can track which food belongs to which table
this.mouthOpen = true;
Opens the customer's mouth as they start eating
this.eatingTimer = 20;
Sets the eating timer to 20 frames to animate the open mouth briefly
Food (Class)
The Food class is simple: it stores position, emoji, and state. The eaten flag is crucial—it prevents the food from being drawn and allows draw() to garbage-collect it. The inKitchen flag prevents kitchen food from being deleted prematurely.
class Food {
constructor(x, y, emoji, inKitchen = false) {
this.x = x;
this.y = y;
this.emoji = emoji;
this.size = random(20, 36); // Random size for variety
this.eaten = false;
this.inKitchen = inKitchen; // Flag to indicate if food is in the kitchen
}
display() {
if (!this.eaten) {
textSize(this.size);
textAlign(CENTER, CENTER);
text(this.emoji, this.x, this.y);
}
}
isEaten() {
return this.eaten;
}
setEaten() {
this.eaten = true;
}
}
Line-by-line explanation (12 lines)
🔧 Subcomponents:
function-callFood Constructorconstructor(x, y, emoji, inKitchen = false) {
Initializes a Food object with position, emoji, size, and flags
Draws the food emoji only if it hasn't been eaten yet
this.x = x;
Stores the food's x position
this.y = y;
Stores the food's y position
this.emoji = emoji;
Stores the emoji string (e.g., '🍕' or '🍰')
this.size = random(20, 36);
Picks a random text size between 20 and 36 pixels so each food item looks slightly different
this.eaten = false;
Initializes eaten to false—food starts uneaten
this.inKitchen = inKitchen;
Stores the inKitchen flag (defaults to false); kitchen food is treated differently when deleted
if (!this.eaten) {
Only draw the food if it hasn't been eaten yet (prevents invisible food from lingering)
textSize(this.size);
Sets the text size to this food's random size
textAlign(CENTER, CENTER);
Centers the text both horizontally and vertically so the emoji is drawn at (x, y) exactly
text(this.emoji, this.x, this.y);
Draws the emoji string at position (x, y)
return this.eaten;
Returns true if the food has been eaten, false otherwise
this.eaten = true;
Marks the food as eaten (called when a customer finishes)
Table (Class)
The Table class manages seating. Each table has an array of chair objects (not full objects, just plain objects with properties). The findEmptyChair() method is crucial—it returns the index of the first available seat, or -1 if full. Notice how chairs store references to customers and food, creating a web of connections that tracks the entire state of the restaurant.
🔬 This loop places the first chair on the left (i === 0) and all others on the right. What happens if you change it to place chairs in a circle around the table instead? Try changing chairX to use Math.cos and Math.sin with an angle based on i—can you arrange four chairs around a table?
// Create chairs around the table
for (let i = 0; i < numChairs; i++) {
let chairX = this.x + (i === 0 ? -this.width / 2 - 20 : this.width / 2 + 20);
let chairY = this.y;
this.chairs.push({ x: chairX, y: chairY, occupied: false, customer: null, food: null });
}
class Table {
constructor(x, y, numChairs) {
this.x = x;
this.y = y;
this.width = 120;
this.height = 80;
this.chairs = [];
// Create chairs around the table
for (let i = 0; i < numChairs; i++) {
let chairX = this.x + (i === 0 ? -this.width / 2 - 20 : this.width / 2 + 20);
let chairY = this.y;
this.chairs.push({ x: chairX, y: chairY, occupied: false, customer: null, food: null });
}
}
display() {
// Table
fill(139, 69, 19); // Brown table
stroke(0);
rect(this.x - this.width / 2, this.y - this.height / 2, this.width, this.height, 5);
// Chairs
for (let chair of this.chairs) {
fill(100); // Gray chair
noStroke();
ellipse(chair.x, chair.y, 30, 30);
if (chair.occupied) {
fill(255, 0, 0, 100); // Red overlay for occupied chairs
ellipse(chair.x, chair.y, 30, 30);
}
}
}
findEmptyChair() {
for (let i = 0; i < this.chairs.length; i++) {
if (!this.chairs[i].occupied) {
return i; // Return the index of the empty chair
}
}
return -1; // No empty chair found
}
}
Line-by-line explanation (22 lines)
🔧 Subcomponents:
function-callTable Constructorconstructor(x, y, numChairs) {
Creates a table at (x, y) with a specified number of chairs positioned to the left and right
for-loopChair Creation Loopfor (let i = 0; i < numChairs; i++) {
let chairX = this.x + (i === 0 ? -this.width / 2 - 20 : this.width / 2 + 20);
let chairY = this.y;
this.chairs.push({ x: chairX, y: chairY, occupied: false, customer: null, food: null });
}
Creates numChairs chair objects: the first chair is on the left, the second on the right
Creates a chair object with position, occupied flag (false initially), customer reference (null), and food reference (null), then adds it to the chairs array
fill(139, 69, 19);
Sets the fill color to brown (RGB: 139, 69, 19) for the table
stroke(0);
Sets the stroke color to black for the table outline
If all tables are permanently full, customers wander forever and never leave. They consume memory indefinitely.
💡 Add a max wander timer; if a customer hasn't found a seat after 600 frames (10 seconds), move them to LEAVING state so they eventually exit.
PERFORMANCEdraw() function
The customer loop and food loop iterate backwards, which is safe but less intuitive. More importantly, there's no limit to how long the customers array can grow if customers are added faster than they leave.
💡 Consider adding a hard cap or pool recycling for customers to prevent memory issues in long-running sketches.
STYLECustomer class constructor
Properties like this.size and this.speed are randomized, but there's no upper bound check. A customer could be extremely tall or fast by chance.
💡 Consider using constrain() to ensure speeds and sizes stay in a reasonable range, or document the ranges clearly.
FEATURETable class
Tables always have exactly 2 chairs, even though the constructor accepts a numChairs parameter that is used but then ignored during chair positioning (all non-first chairs go to the right).
💡 Enhance the chair positioning logic to arrange multiple chairs in a circle or semicircle around the table for better realism and scalability.
BUGFood class and draw() loop
Kitchen-created food (inKitchen = true) is never removed from the foodItems array, it just gets eaten and hidden. Over thousands of clicks, kitchen food accumulates invisibly.
💡 Either modify the condition in draw() to also remove kitchen food after eating, or implement a separate kitchen inventory system with a max limit.
What visual elements are created in the p5.js sketch?
The sketch visually represents a restaurant scene featuring a kitchen, multiple tables, and animated customers using emojis to depict food items.
How can users interact with the creative coding sketch?
Currently, the sketch does not support user interaction; it primarily displays a simulation of customers arriving and interacting within a restaurant setting.
What creative coding concepts are demonstrated in this sketch?
This sketch showcases object-oriented programming through the use of Customer and Table classes, as well as dynamic array manipulation to manage customer behavior.