setup()
setup() runs once when the sketch starts. In DOM-based p5.js sketches like this one, setup() is where you build your entire interface using createDiv(), createButton(), createInput() and similar functions, then wire up event handlers with .mousePressed(), .changed(), and .input().
🔬 This toggles the settings panel by flipping settingsVisible true/false each click. What happens if you change "block" to "flex" in the true case - does the panel look any different, and why might 'flex' matter for panels with rows of controls?
settingsButton.mousePressed(() => {
settingsVisible = !settingsVisible;
settingsPanel.style("display", settingsVisible ? "block" : "none");
});
function setup() {
noCanvas();
// --- Create Main Flex Container ---
mainContainer = createDiv();
mainContainer.style("display", "flex");
mainContainer.style("flex-direction", "column");
mainContainer.style("height", "100vh"); // Make it full height
// --- Create the Title Element ---
titleElement = createElement('h1', 'Potato AI 2.0 🥔');
titleElement.style("text-align", "center");
titleElement.style("padding", "10px");
titleElement.style("margin", "0");
titleElement.style("background-color", "#f5e0b3");
titleElement.style("border-bottom", "1px solid #d2b48c");
titleElement.parent(mainContainer);
// --- Subtitle under the title ---
subtitleElement = createElement('p', '©️CDWW inc 2026');
subtitleElement.style("text-align", "center");
subtitleElement.style("margin", "0 0 8px 0");
subtitleElement.style("padding", "2px 0 6px 0");
subtitleElement.style("font-size", "12px");
subtitleElement.style("background-color", "#f5e0b3");
subtitleElement.style("border-bottom", "1px solid #d2b48c");
subtitleElement.parent(mainContainer);
// --- Controls area (Settings + Play GIF) ---
let controlsArea = createDiv();
controlsArea.style("display", "flex");
controlsArea.style("justify-content", "center");
controlsArea.style("gap", "10px");
controlsArea.style("padding", "8px");
controlsArea.style("background-color", "#f3d6a1");
controlsArea.parent(mainContainer);
settingsButton = createButton("Settings");
settingsButton.style("padding", "6px 12px");
settingsButton.style("border-radius", "8px");
settingsButton.style("border", "1px solid #d2b48c");
settingsButton.style("background-color", "#fff3cd");
settingsButton.style("cursor", "pointer");
settingsButton.parent(controlsArea);
playGifButton = createButton("Play Potato GIF");
playGifButton.style("padding", "6px 12px");
playGifButton.style("border-radius", "8px");
playGifButton.style("border", "1px solid #d2b48c");
playGifButton.style("background-color", "#ffe4b5");
playGifButton.style("cursor", "pointer");
playGifButton.parent(controlsArea);
// --- Chat Container ---
chatContainer = createDiv();
chatContainer.style("flex-grow", "1"); // Make chatContainer fill available space
chatContainer.style("overflow-y", "auto");
chatContainer.style("padding", "10px");
chatContainer.style("display", "flex");
chatContainer.style("flex-direction", "column");
chatContainer.style("background-color", "#f5e0b3");
chatContainer.parent(mainContainer); // Parent to mainContainer
// --- Input Area ---
inputArea = createDiv();
inputArea.style("display", "flex");
inputArea.style("padding", "10px");
inputArea.style("background-color", "#f5e0b3");
inputArea.parent(mainContainer); // Parent to mainContainer
inputBox = createInput();
inputBox.style("flex", "1");
inputBox.style("padding", "10px");
inputBox.style("font-size", "16px");
inputBox.parent(inputArea);
sendButton = createButton("Send");
sendButton.style("padding", "10px 20px");
sendButton.style("margin-left", "5px");
sendButton.style("border-radius", "8px");
sendButton.style("border", "1px solid #d2b48c");
sendButton.style("background-color", "#fff3cd");
sendButton.parent(inputArea);
sendButton.mousePressed(sendMessage);
// Store the listener function
keypressListener = function(e) {
if (e.key === "Enter") sendMessage();
};
inputBox.elt.addEventListener("keypress", keypressListener);
// --- Settings panel (hidden by default) ---
settingsPanel = createDiv();
settingsPanel.style("position", "fixed");
settingsPanel.style("top", "60px");
settingsPanel.style("right", "10px");
settingsPanel.style("padding", "10px");
settingsPanel.style("background-color", "#fffaf0");
settingsPanel.style("border", "1px solid #d2b48c");
settingsPanel.style("border-radius", "8px");
settingsPanel.style("box-shadow", "0 2px 6px rgba(0,0,0,0.15)");
settingsPanel.style("z-index", "900");
settingsPanel.style("display", "none");
settingsPanel.parent(document.body);
let settingsTitle = createElement('h3', 'Settings');
settingsTitle.style("margin", "0 0 8px 0");
settingsTitle.style("font-size", "16px");
settingsTitle.parent(settingsPanel);
smartModeCheckbox = createCheckbox(' Smart mode', false);
smartModeCheckbox.parent(settingsPanel);
smartModeCheckbox.style("margin-bottom", "8px");
smartModeCheckbox.changed(function() {
smartModeEnabled = smartModeCheckbox.checked();
if (smartModeEnabled) {
addMessage("Smart mode activated. This potato is now extra crispy 🥔✨", "ai");
} else {
addMessage("Smart mode deactivated. Back to simple spud life.", "ai");
}
});
let bgLabel = createP('Background color');
bgLabel.style("margin", "8px 0 4px 0");
bgLabel.style("font-size", "14px");
bgLabel.parent(settingsPanel);
bgColorPicker = createColorPicker('#f5e0b3');
bgColorPicker.parent(settingsPanel);
bgColorPicker.style("width", "100%");
bgColorPicker.input(function() {
const c = bgColorPicker.value();
document.body.style.backgroundColor = c;
if (chatContainer) {
chatContainer.style('background-color', c);
}
if (inputArea) {
inputArea.style('background-color', c);
}
});
// NEW: "Mess with Potato AI's code" button
let chaosLabel = createP('Danger zone: mess with Potato AI');
chaosLabel.style("margin", "10px 0 4px 0");
chaosLabel.style("font-size", "13px");
chaosLabel.style("color", "#b00000");
chaosLabel.parent(settingsPanel);
codeChaosButton = createButton("Mess with Potato AI's code");
codeChaosButton.parent(settingsPanel);
codeChaosButton.style("width", "100%");
codeChaosButton.style("margin-top", "4px");
codeChaosButton.style("padding", "6px");
codeChaosButton.style("background-color", "#ffcccc");
codeChaosButton.style("border", "1px solid #d2b48c");
codeChaosButton.style("border-radius", "6px");
codeChaosButton.mousePressed(() => {
codeChaosEnabled = !codeChaosEnabled;
if (codeChaosEnabled) {
addMessage("Uh oh... you just messed with my code. Expect some weird glitches now.", "ai");
codeChaosButton.html("Fix Potato AI's code");
showCodeChaosSequence(); // trigger GIF then static image
} else {
addMessage("Okay, I think my code is back to normal. I hope.", "ai");
codeChaosButton.html("Mess with Potato AI's code");
}
});
// Toggle settings panel visibility
settingsButton.mousePressed(() => {
settingsVisible = !settingsVisible;
settingsPanel.style("display", settingsVisible ? "block" : "none");
});
// Play GIF button behavior
playGifButton.mousePressed(handlePlayGifButton);
// --- Add the X Button (now Rickroll button) ---
let exitButton = createButton('X');
exitButton.style('position', 'fixed');
exitButton.style('top', '10px');
exitButton.style('right', '10px');
exitButton.style('padding', '10px 15px');
exitButton.style('font-size', '18px');
exitButton.style('background-color', '#ff0000'); // Red for exit
exitButton.style('color', 'white');
exitButton.style('border', 'none');
exitButton.style('border-radius', '5px');
exitButton.style('cursor', 'pointer');
exitButton.style('z-index', '1000'); // Ensure it's on top of other elements
// Instead of leaving, X now Rickrolls you
exitButton.mousePressed(showRickrollOverlay);
}
Line-by-line explanation (14 lines)
🔧 Subcomponents:
if (smartModeEnabled) { addMessage("Smart mode activated..."); } else { addMessage("Smart mode deactivated..."); }
Announces in the chat whenever smart mode is switched on or off
if (codeChaosEnabled) { ... showCodeChaosSequence(); } else { ... }
Flips glitch mode on/off and launches the Matrix-style overlay sequence the first time it's turned on
settingsPanel.style("display", settingsVisible ? "block" : "none");
Shows or hides the settings panel based on the settingsVisible boolean
noCanvas();- Tells p5.js not to create a drawing canvas at all, since this whole sketch is built from HTML elements instead.
mainContainer = createDiv();- Creates the outer wrapper div that everything else (title, chat, input) gets attached to.
mainContainer.style("height", "100vh");- Forces the wrapper to fill the full height of the browser viewport.
titleElement = createElement('h1', 'Potato AI 2.0 🥔');- Creates an actual <h1> heading element with the sketch's title text.
chatContainer.style("flex-grow", "1");- Makes the chat area expand to fill all remaining vertical space, pushing the input bar to the bottom.
sendButton.mousePressed(sendMessage);- Wires the Send button so clicking it calls sendMessage().
keypressListener = function(e) { if (e.key === "Enter") sendMessage(); };- Stores a function (so it can be removed later) that calls sendMessage() whenever the Enter key is pressed.
inputBox.elt.addEventListener("keypress", keypressListener);- Attaches the raw DOM event listener to the underlying input element, since p5 doesn't have a built-in 'Enter key' handler.
settingsPanel.style("display", "none");- Hides the settings panel by default until the Settings button is clicked.
smartModeCheckbox.changed(function() { ... });- Registers a callback that runs whenever the checkbox is ticked or unticked, updating smartModeEnabled and posting a chat message.
bgColorPicker.input(function() { ... });- Runs live as the user drags the color picker, instantly updating the page and chat background colors.
codeChaosButton.mousePressed(() => { ... });- Toggles the codeChaosEnabled flag and either starts the chaos sequence or announces things are back to normal.
settingsButton.mousePressed(() => { settingsVisible = !settingsVisible; ... });- Flips a boolean each click and shows/hides the settings panel to match.
exitButton.mousePressed(showRickrollOverlay);- Instead of closing anything, clicking the red X button launches the Rickroll overlay function.