Sketch 2026-02-22 22:27

This sketch creates an interactive fighting game where the player controls Spider-Man using keyboard and mouse to battle an AI-controlled Deadpool. The game features physics-based movement, web-swinging mechanics with momentum, health systems, and a turn-based attack system with cooldowns.

🧪 Try This!

Experiment with the code by making these changes:

  1. Make Deadpool twice as fast — Deadpool will chase Spider-Man more aggressively, making the game harder and requiring faster reflexes to escape.
  2. Make Spider-Man super jump — Spider-Man will bounce much higher in the air, allowing you to escape Deadpool more easily and reach higher areas.
  3. Reduce Deadpool's health to 50 — The game will end much faster—Spider-Man only needs to land a few hits to win, making victories feel quicker and easier.
  4. Make the web swing stronger — The spring-like force pulling Spider-Man toward the web point will be stronger, making swinging feel tighter and more responsive.
  5. Change the sky color to purple — The background shifts from light gray to purple, creating a dramatic dusk or alien atmosphere for the battle.
  6. Triple Spider-Man's attack damage — Each web punch will deal 30 damage instead of 10, letting you defeat Deadpool in just 4 hits instead of 12.
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch creates a full interactive fighting game where you control Spider-Man to battle Deadpool on a city rooftop. What makes it visually compelling is the web-swinging mechanic: click and drag to swing Spider-Man toward a point, and he'll swing with pendulum-like physics, carrying momentum when you release. The game combines several advanced p5.js techniques: physics simulation with gravity and drag, object-oriented code with character objects, animation loops with state management, and keyboard/mouse event handling.

The code is organized around two character objects—spiderman and deadpool—each with display(), move(), and attack() methods. The sketch also manages game state (start screen, playing, game over), health bars, AI pathfinding, and collision detection. By studying it, you will learn how to structure a complete game with multiple interacting systems, how object methods make code reusable and clean, and how simple physics formulas create convincing motion.

⚙️ How It Works

  1. When the sketch loads, setup() creates a full-window canvas and calls resetGame() to position both characters on opposite sides of a city rooftop ground.
  2. On every frame, draw() clears the background, draws the city and ground, then checks the current gameState: if 'start', it shows the title and controls; if 'playing', it updates both characters, displays them, checks for winner; if 'gameOver', it shows who won.
  3. Spider-Man moves left/right with arrow keys, each key press setting his horizontal velocity vx which persists and is reduced by friction each frame—this creates smooth, controllable motion.
  4. When you click the mouse, mousePressed() calls spiderman.startSwing(mouseX, mouseY), which enters swinging mode: a spring-like force pulls him toward the click point while gravity acts downward, creating realistic pendulum swinging. When you release, mouseReleased() calls endSwing(), which converts the swing velocity into regular jumping velocity so Spider-Man flies away with the momentum he built up.
  5. Deadpool uses simple AI: every frame, deadpool.move() checks the distance to Spider-Man and walks toward him. When within range, both characters can attack using the spacebar (player) or automatically (Deadpool), each attack dealing damage and going on cooldown to prevent spamming.
  6. The game ends when either character's health reaches 0, triggering the game-over screen. Pressing any key or clicking restarts the game by calling resetGame() and setting gameState to 'playing'.

🎓 Concepts You'll Learn

Physics simulation with gravity and dragObject-oriented code structure with methodsGame state managementKeyboard and mouse event handlingCollision detection and range checkingAI pathfinding and attack logicHealth systems and cooldownsMomentum and velocity carry-over

📝 Code Breakdown

setup()

setup() runs once when the sketch starts. Here we initialize the canvas and game variables. Putting initialization in a separate resetGame() function makes it easy to restart without reloading the page.

function setup() {
  createCanvas(windowWidth, windowHeight);
  resetGame();
}
Line-by-line explanation (2 lines)
createCanvas(windowWidth, windowHeight);
Creates a canvas that fills the entire browser window—windowWidth and windowHeight automatically adjust if the window is resized
resetGame();
Calls the resetGame function to set both characters to their starting positions and health values

draw()

draw() is the heartbeat of your game—it runs 60 times per second and contains all game logic. State variables like gameState control what happens each frame, making it easy to switch between different screens (start, playing, paused, game over) without reloading.

🔬 These conditionals control what happens on each frame based on gameState. What if you add another else-if branch that checks for gameState === 'paused' and displays a pause screen instead of updating? Try creating a new game state by adding: } else if (gameState === 'paused') { displayPauseScreen(); }

  if (gameState === 'start') {
    displayStartScreen();
  } else if (gameState === 'playing') {
    // Update and display characters
    spiderman.move();
    deadpool.move(spiderman.x, spiderman.y);
function draw() {
  background(220); // Light gray background
  drawCityBackground(); // Simplified city background
  drawGround(); // Draw the ground

  if (gameState === 'start') {
    displayStartScreen();
  } else if (gameState === 'playing') {
    // Update and display characters
    spiderman.move();
    deadpool.move(spiderman.x, spiderman.y); // Deadpool tracks Spiderman

    spiderman.display();
    deadpool.display();

    // Display health bars
    displayHealthBar(spiderman.x, spiderman.y - SPIDERMAN_SIZE, spiderman.health, SPIDERMAN_HEALTH, color(0, 200, 0));
    displayHealthBar(deadpool.x, deadpool.y - DEADPOOL_SIZE, deadpool.health, DEADPOOL_HEALTH, color(200, 0, 0));

    // Check for game over
    if (spiderman.health <= 0 || deadpool.health <= 0) {
      gameState = 'gameOver';
    }
  } else if (gameState === 'gameOver') {
    displayGameOverScreen();
  }
}
Line-by-line explanation (10 lines)

🔧 Subcomponents:

conditional Start Screen State if (gameState === 'start') {

Displays title and controls before the player begins

conditional Playing State Logic } else if (gameState === 'playing') {

Updates all game objects, displays characters and health bars, checks for game end conditions

conditional Game Over State } else if (gameState === 'gameOver') {

Shows the winner and prompts player to restart

conditional Victory Condition Check if (spiderman.health <= 0 || deadpool.health <= 0) {

Detects when either character dies and transitions to game over screen

background(220);
Fills the entire canvas with light gray, clearing the previous frame so characters don't leave trails
drawCityBackground();
Calls the function that draws tall buildings behind the action—these stay the same every frame
drawGround();
Draws the green ground platform where characters stand—rebuilds it each frame as part of the background layer
spiderman.move();
Updates Spider-Man's position based on keyboard input and physics (gravity, jump, swinging)
deadpool.move(spiderman.x, spiderman.y);
Updates Deadpool's position—passes Spider-Man's coordinates so Deadpool's AI can chase him
spiderman.display();
Draws Spider-Man at his current position with his red body, white eyes, and blue spider symbol
deadpool.display();
Draws Deadpool at his current position with his red body, white eyes, and black mouth
displayHealthBar(spiderman.x, spiderman.y - SPIDERMAN_SIZE, spiderman.health, SPIDERMAN_HEALTH, color(0, 200, 0));
Shows Spider-Man's green health bar above his head, displaying current health and maximum health
if (spiderman.health <= 0 || deadpool.health <= 0) {
Tests whether either character's health dropped to 0 or below, triggering game end
gameState = 'gameOver';
Transitions the game to the game-over screen by changing the state variable

keyPressed()

keyPressed() is called once every time a key is pressed. It's perfect for one-time actions like jumping or starting a game. For continuous movement (like holding left/right), use keyIsDown() in the move() function instead—that's why the code checks keyIsDown(LEFT_ARROW) inside spiderman.move(), not here.

🔬 These two conditionals handle different keys independently. What if you wanted a WASD control scheme? Replace the UP_ARROW check with a check for 'W' (keyCode 87) and add new conditionals for LEFT_ARROW→'A' and RIGHT_ARROW→'D'. What control scheme feels most natural?

    if (keyCode === UP_ARROW) {
      spiderman.jump();
    }
    if (keyCode === 32) { // Spacebar for attack
      spiderman.attack(deadpool);
    }
function keyPressed() {
  if (gameState === 'playing') {
    if (keyCode === UP_ARROW) {
      spiderman.jump();
    }
    if (keyCode === 32) { // Spacebar for attack
      spiderman.attack(deadpool);
    }
  } else if (gameState === 'start' || gameState === 'gameOver') {
    // Any key to restart/start
    resetGame();
    gameState = 'playing';
  }
}
Line-by-line explanation (8 lines)

🔧 Subcomponents:

conditional In-Game Controls if (gameState === 'playing') {

Checks if the game is running, then handles jump and attack inputs

conditional Jump Command if (keyCode === UP_ARROW) {

Detects up arrow press and calls Spider-Man's jump method

conditional Attack Command if (keyCode === 32) { // Spacebar for attack

Detects spacebar press and executes an attack on Deadpool

conditional Restart on Any Key } else if (gameState === 'start' || gameState === 'gameOver') {

Allows the player to start a new game by pressing any key during the start or game-over screens

if (gameState === 'playing') {
Only processes game commands (jump, attack) when the game is actively running; prevents accidental actions on the start screen
if (keyCode === UP_ARROW) {
Checks if the key pressed was the up arrow—keyCode is a p5.js variable that stores the numerical code of the last key pressed
spiderman.jump();
Calls Spider-Man's jump method, which checks if he's already jumping and applies upward velocity if he can jump
if (keyCode === 32) { // Spacebar for attack
Checks if spacebar was pressed (keyCode 32)—the comment explains what this mysterious number means
spiderman.attack(deadpool);
Calls Spider-Man's attack method and passes deadpool as the target, dealing damage if within range and cooldown has passed
} else if (gameState === 'start' || gameState === 'gameOver') {
If the game is not playing (either on start or game-over screen), any key press triggers a restart
resetGame();
Resets both characters' positions, health, and velocities to their starting state
gameState = 'playing';
Changes the state to 'playing', which causes draw() to start updating characters and checking for attacks

mousePressed()

mousePressed() fires once when the mouse button goes down. Paired with mouseReleased() (which fires when the button goes up), you can detect clicks and drags. Here, clicking sets the web target and releasing breaks the web.

function mousePressed() {
  if (gameState === 'playing') {
    // Start web swinging
    spiderman.startSwing(mouseX, mouseY);
  } else if (gameState === 'start' || gameState === 'gameOver') {
    // Click to restart/start
    resetGame();
    gameState = 'playing';
  }
}
Line-by-line explanation (5 lines)

🔧 Subcomponents:

conditional Web Swing Initiation if (gameState === 'playing') {

When playing, a mouse click tells Spider-Man to start swinging toward the clicked position

conditional Click to Start } else if (gameState === 'start' || gameState === 'gameOver') {

Clicking on start or game-over screens restarts the game

if (gameState === 'playing') {
Only activates web swinging during actual gameplay; prevents accidental swings on menus
spiderman.startSwing(mouseX, mouseY);
Passes the click position (mouseX, mouseY) to Spider-Man's startSwing method, which engages physics-based web swinging toward that point
} else if (gameState === 'start' || gameState === 'gameOver') {
If not playing, treat any click as a request to start the game
resetGame();
Resets all character variables to starting values
gameState = 'playing';
Transitions to the playing state so draw() updates game logic instead of showing screens

mouseReleased()

mouseReleased() is the counterpart to mousePressed()—it fires when the mouse button is released. Together they enable drag-and-swing mechanics: press to start, drag to aim, release to launch.

function mouseReleased() {
  if (gameState === 'playing') {
    // End web swinging
    spiderman.endSwing();
  }
}
Line-by-line explanation (2 lines)
if (gameState === 'playing') {
Only breaks the web during gameplay; prevents errors if the mouse is released on a menu screen
spiderman.endSwing();
Calls the endSwing method, which detaches from the web and converts swing velocity into regular jumping velocity for momentum carry-over

windowResized()

windowResized() is called automatically whenever the browser window size changes. Without it, your canvas and characters would get cut off or positioned incorrectly. This function keeps the game playable at any screen size.

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
  // Reset character positions only if not currently playing
  if (gameState !== 'playing') {
    resetGame();
  }
  // Otherwise, characters will be constrained by the new canvas size
  spiderman.x = constrain(spiderman.x, SPIDERMAN_SIZE / 2, width - SPIDERMAN_SIZE / 2);
  spiderman.y = constrain(spiderman.y, SPIDERMAN_SIZE / 2, height - GROUND_LEVEL - SPIDERMAN_SIZE / 2);
  deadpool.x = constrain(deadpool.x, DEADPOOL_SIZE / 2, width - DEADPOOL_SIZE / 2);
  deadpool.y = constrain(deadpool.y, DEADPOOL_SIZE / 2, height - GROUND_LEVEL - DEADPOOL_SIZE / 2);
}
Line-by-line explanation (6 lines)

🔧 Subcomponents:

calculation Canvas Resizing resizeCanvas(windowWidth, windowHeight);

Adjusts the canvas to match the new window size when the browser window is resized

conditional Reset If Not Playing if (gameState !== 'playing') {

Resets the game during menus but preserves positions and game progress during active play

calculation Boundary Constraining spiderman.x = constrain(spiderman.x, SPIDERMAN_SIZE / 2, width - SPIDERMAN_SIZE / 2);

Keeps characters visible on-screen after a resize by forcing their positions within valid bounds

resizeCanvas(windowWidth, windowHeight);
Adjusts the p5.js canvas dimensions to the new window size—this function is called automatically when the browser window is resized
if (gameState !== 'playing') {
Checks if the game is NOT actively running; if true, it's safe to reset without interrupting gameplay
resetGame();
Repositions both characters to their starting locations since the canvas dimensions have changed
spiderman.x = constrain(spiderman.x, SPIDERMAN_SIZE / 2, width - SPIDERMAN_SIZE / 2);
Uses constrain() to force Spider-Man's x position between valid bounds—SPIDERMAN_SIZE / 2 is the left edge (character radius), width - SPIDERMAN_SIZE / 2 is the right edge
deadpool.x = constrain(deadpool.x, DEADPOOL_SIZE / 2, width - DEADPOOL_SIZE / 2);
Similarly constrains Deadpool's x position to prevent him from disappearing off the edge of the newly-sized canvas
spiderman.y = constrain(spiderman.y, SPIDERMAN_SIZE / 2, height - GROUND_LEVEL - SPIDERMAN_SIZE / 2);
Constrains Spider-Man's y position vertically—keeps him between the top and the ground level

resetGame()

resetGame() sets all character properties back to starting values. This function is called when setup() first runs and again whenever the player restarts. By keeping all reset logic in one place, you avoid bugs from forgetting to reset some variable.

function resetGame() {
  spiderman.x = width * 0.2;
  spiderman.y = height - GROUND_LEVEL - SPIDERMAN_SIZE / 2;
  spiderman.vx = 0; // Initialize horizontal velocity
  spiderman.vy = 0;
  spiderman.health = SPIDERMAN_HEALTH;
  spiderman.isJumping = false;
  spiderman.isSwinging = false;
  spiderman.swingVX = 0;
  spiderman.swingVY = 0;
  spiderman.lastAttackTime = 0;

  deadpool.x = width * 0.8;
  deadpool.y = height - GROUND_LEVEL - DEADPOOL_SIZE / 2;
  deadpool.vy = 0;
  deadpool.health = DEADPOOL_HEALTH;
  deadpool.lastAttackTime = 0;
}
Line-by-line explanation (10 lines)

🔧 Subcomponents:

calculation Spider-Man Reset spiderman.x = width * 0.2;

Positions Spider-Man at 20% from the left edge of the canvas

calculation Deadpool Reset deadpool.x = width * 0.8;

Positions Deadpool at 80% from the left edge, placing him on the opposite side

spiderman.x = width * 0.2;
Sets Spider-Man's x position to 20% across the canvas width—multiplying by 0.2 places him 1/5 of the way from the left
spiderman.y = height - GROUND_LEVEL - SPIDERMAN_SIZE / 2;
Places Spider-Man on the ground by calculating: total height minus the ground reserve space minus half his size (so his center touches the ground)
spiderman.vx = 0;
Resets horizontal velocity to 0 so he doesn't start moving when the game begins
spiderman.vy = 0;
Resets vertical velocity to 0 so he isn't falling or jumping at game start
spiderman.health = SPIDERMAN_HEALTH;
Restores Spider-Man's health to the constant SPIDERMAN_HEALTH (100) after a previous game
spiderman.isJumping = false;
Clears the jump flag so he can immediately jump when the player presses the up arrow
spiderman.isSwinging = false;
Ensures Spider-Man isn't still in swing mode from a previous game
spiderman.lastAttackTime = 0;
Resets the attack cooldown timer to 0, allowing the first attack immediately when the game starts
deadpool.x = width * 0.8;
Positions Deadpool 80% across the canvas—far from Spider-Man on the opposite side
deadpool.y = height - GROUND_LEVEL - DEADPOOL_SIZE / 2;
Matches Spider-Man's ground placement calculation so both start on the same ground level

displayStartScreen()

displayStartScreen() only draws text—it doesn't handle input (that's in keyPressed() and mousePressed()). By separating display logic from input logic, your code stays organized and reusable.

function displayStartScreen() {
  fill(0);
  noStroke();
  textAlign(CENTER, CENTER);
  textSize(48);
  text("Spider-Man vs. Deadpool", width / 2, height / 2 - 50);
  textSize(24);
  text("Press Any Key or Click to Start", width / 2, height / 2);
  textSize(18);
  text("Controls:", width / 2, height / 2 + 50);
  text("Left/Right Arrows: Move Spidey", width / 2, height / 2 + 80);
  text("Up Arrow: Jump", width / 2, height / 2 + 110);
  text("Spacebar: Attack", width / 2, height / 2 + 140);
  text("Mouse Press/Release: Web Swing", width / 2, height / 2 + 170);
}
Line-by-line explanation (8 lines)

🔧 Subcomponents:

calculation Text Formatting Setup fill(0); noStroke(); textAlign(CENTER, CENTER);

Sets all text to black, center-aligned, with no outline—these settings apply to all text() calls that follow

fill(0);
Sets the text color to black (RGB 0, 0, 0)
noStroke();
Removes any outline from text, making it solid rather than hollow
textAlign(CENTER, CENTER);
Centers text horizontally and vertically around the x,y coordinates you pass to text()—makes positioning much simpler
textSize(48);
Sets font size to 48 pixels for the title
text("Spider-Man vs. Deadpool", width / 2, height / 2 - 50);
Draws the title text centered horizontally and positioned 50 pixels above the vertical center of the canvas
textSize(24);
Reduces font size to 24 pixels for the subtitle
text("Press Any Key or Click to Start", width / 2, height / 2);
Draws instructions text at the vertical center of the canvas
textSize(18);
Reduces font size again to 18 pixels for the control labels

displayGameOverScreen()

displayGameOverScreen() shows the outcome of the battle. By checking which character's health is 0 or below, it determines the winner. This is a simple but effective use of conditional logic to create different outcomes.

🔬 This logic builds a message string that says who won. What if you added a third condition: if both characters die at the exact same frame, display 'Double KO!' instead? Try adding: } else { message += " Double KO!"; }

  let message = "Game Over!";
  if (spiderman.health <= 0) {
    message += " Deadpool Wins!";
  } else if (deadpool.health <= 0) {
    message += " Spider-Man Wins!";
  }
function displayGameOverScreen() {
  fill(0);
  noStroke();
  textAlign(CENTER, CENTER);
  textSize(48);

  let message = "Game Over!";
  if (spiderman.health <= 0) {
    message += " Deadpool Wins!";
  } else if (deadpool.health <= 0) {
    message += " Spider-Man Wins!";
  }
  text(message, width / 2, height / 2 - 50);

  textSize(24);
  text("Press Any Key or Click to Play Again", width / 2, height / 2);
}
Line-by-line explanation (8 lines)

🔧 Subcomponents:

conditional Winner Determination if (spiderman.health <= 0) { message += " Deadpool Wins!"; } else if (deadpool.health <= 0) { message += " Spider-Man Wins!"; }

Checks which character died and appends the winner's name to the message

fill(0);
Sets text color to black
textSize(48);
Large font for the game-over message
let message = "Game Over!";
Initializes the message string with a base phrase
if (spiderman.health <= 0) {
Checks if Spider-Man is dead
message += " Deadpool Wins!";
If Spider-Man is dead, appends Deadpool's victory to the message
} else if (deadpool.health <= 0) {
Checks if Deadpool is dead (only if Spider-Man is still alive)
message += " Spider-Man Wins!";
If Deadpool is dead, appends Spider-Man's victory to the message
text(message, width / 2, height / 2 - 50);
Displays the complete message (either 'Game Over! Deadpool Wins!' or 'Game Over! Spider-Man Wins!') at the screen center

displayHealthBar(x, y, currentHealth, maxHealth, barColor)

displayHealthBar() is a reusable function that takes parameters for position, current/max health, and color. This design makes it work for both Spider-Man and Deadpool without code duplication. The key insight is multiplying the bar's width by healthRatio to scale it smoothly.

🔬 This code draws two rectangles: a gray background and a colored foreground. The foreground width shrinks based on healthRatio. What if you wanted to show damage as a different color? Try adding a third rectangle between them that shows the health lost (width = barWidth * (1 - healthRatio)) in red.

  // Background bar
  fill(100);
  noStroke();
  rect(x - barWidth / 2, y - barHeight, barWidth, barHeight);

  // Health bar
  fill(barColor);
  noStroke();
  rect(x - barWidth / 2, y - barHeight, barWidth * healthRatio, barHeight);
function displayHealthBar(x, y, currentHealth, maxHealth, barColor) {
  let barWidth = 80;
  let barHeight = 8;
  let healthRatio = currentHealth / maxHealth;

  // Background bar
  fill(100);
  noStroke();
  rect(x - barWidth / 2, y - barHeight, barWidth, barHeight);

  // Health bar
  fill(barColor);
  noStroke();
  rect(x - barWidth / 2, y - barHeight, barWidth * healthRatio, barHeight);

  // Health text
  fill(0);
  textSize(10);
  textAlign(CENTER, BOTTOM);
  text(ceil(currentHealth) + "/" + maxHealth, x, y - barHeight - 2);
}
Line-by-line explanation (8 lines)

🔧 Subcomponents:

calculation Health Percentage Calculation let healthRatio = currentHealth / maxHealth;

Divides current health by max health to get a decimal between 0 and 1, representing the percentage of health remaining

calculation Draw Background Bar rect(x - barWidth / 2, y - barHeight, barWidth, barHeight);

Draws the gray background rectangle showing the full width of the health bar

calculation Draw Health Bar rect(x - barWidth / 2, y - barHeight, barWidth * healthRatio, barHeight);

Draws the colored health bar on top, with width scaled by healthRatio so it shrinks as health decreases

let barWidth = 80;
Sets the health bar's total width to 80 pixels
let barHeight = 8;
Sets the health bar's height to 8 pixels—thin and unobtrusive
let healthRatio = currentHealth / maxHealth;
Calculates a percentage: if maxHealth is 100 and currentHealth is 50, healthRatio = 0.5 (50%)
fill(100);
Sets the background bar color to gray (RGB 100, 100, 100)
rect(x - barWidth / 2, y - barHeight, barWidth, barHeight);
Draws the background bar: x - barWidth/2 centers it horizontally, rect() draws a rectangle of barWidth × barHeight
fill(barColor);
Sets the foreground bar color to whatever was passed in (green for Spider-Man, red for Deadpool)
rect(x - barWidth / 2, y - barHeight, barWidth * healthRatio, barHeight);
Draws the health bar on top—crucially, its width is barWidth * healthRatio, so it shrinks as health decreases
text(ceil(currentHealth) + "/" + maxHealth, x, y - barHeight - 2);
Displays the health as text (e.g., '75/100')—ceil() rounds up to the nearest integer so it looks like '75' not '74.3'

drawCityBackground()

drawCityBackground() generates a procedural cityscape using random heights and widths. The while loop ensures buildings fill the entire canvas width. This function redraws every frame, but it's fast enough not to notice because it's just rectangles.

function drawCityBackground() {
  // Simple buildings
  fill(150);
  noStroke();
  let xOffset = 0;
  while (xOffset < width) {
    let buildingHeight = random(height * 0.3, height * 0.7);
    let buildingWidth = random(80, 120); // Slightly more consistent width
    rect(xOffset, height - GROUND_LEVEL - buildingHeight, buildingWidth, buildingHeight);
    xOffset += buildingWidth + random(20, 50); // Add some spacing
  }
}
Line-by-line explanation (8 lines)

🔧 Subcomponents:

while-loop Building Generation Loop while (xOffset < width) {

Loops through the canvas width, creating a new building at each iteration until reaching the right edge

fill(150);
Sets the building color to medium gray (RGB 150, 150, 150)
noStroke();
Removes outlines from rectangles so buildings appear solid
let xOffset = 0;
Starts at the left edge of the canvas and will move right with each building
while (xOffset < width) {
Continues looping and creating buildings as long as xOffset is less than the canvas width
let buildingHeight = random(height * 0.3, height * 0.7);
Picks a random height between 30% and 70% of the canvas height, creating variation
let buildingWidth = random(80, 120);
Picks a random width between 80 and 120 pixels—narrower than height to look like tall buildings
rect(xOffset, height - GROUND_LEVEL - buildingHeight, buildingWidth, buildingHeight);
Draws a building at xOffset position, starting from the ground level and extending upward
xOffset += buildingWidth + random(20, 50);
Moves xOffset to the right by the building's width plus a random gap, creating variable spacing between buildings

drawGround()

drawGround() creates a simple but effective ground with a solid base and texture lines for detail. The texture loop is a great example of how simple repeated geometry can suggest complexity—this technique is used throughout game art.

function drawGround() {
  fill(100, 150, 100); // Greenish-gray for ground
  noStroke();
  rect(0, height - GROUND_LEVEL, width, GROUND_LEVEL);
  // Add some simple lines to indicate texture
  stroke(80, 120, 80);
  strokeWeight(1);
  for (let i = 0; i < width; i += 20) {
    line(i, height - GROUND_LEVEL, i, height);
  }
}
Line-by-line explanation (6 lines)

🔧 Subcomponents:

calculation Ground Rectangle rect(0, height - GROUND_LEVEL, width, GROUND_LEVEL);

Draws the solid green platform at the bottom of the canvas where characters stand

for-loop Texture Lines for (let i = 0; i < width; i += 20) {

Loops across the ground width, drawing vertical lines every 20 pixels to suggest grass texture

fill(100, 150, 100);
Sets ground color to a greenish-gray (RGB: 100 red, 150 green, 100 blue) to look like grass
rect(0, height - GROUND_LEVEL, width, GROUND_LEVEL);
Draws the ground rectangle: starts at x=0, y = height minus GROUND_LEVEL (so it's anchored to the bottom), and spans the full canvas width
stroke(80, 120, 80);
Sets the texture line color to a darker green (RGB: 80, 120, 80)
strokeWeight(1);
Sets texture line thickness to 1 pixel—thin enough to look like fine grass without dominating the ground
for (let i = 0; i < width; i += 20) {
Loops from i=0 to i=width by increments of 20, creating texture lines every 20 pixels
line(i, height - GROUND_LEVEL, i, height);
Draws a vertical line from the top of the ground down to the bottom of the canvas, creating parallel 'grass blade' effect

spiderman.move()

spiderman.move() is the heart of the physics simulation. It handles two very different movement modes: normal walking/jumping and web swinging. The swinging mode uses a spring-like force (proportional to distance) plus gravity to create realistic pendulum motion. The key insight is that acceleration updates velocity, and velocity updates position—applied every frame, this creates smooth animation.

move: function() {
    // Apply swinging physics
    if (this.isSwinging) {
      let dx = this.webTargetX - this.x;
      let dy = this.webTargetY - this.y;
      let angle = atan2(dy, dx);
      let dist = dist(this.x, this.y, this.webTargetX, this.webTargetY);

      // Simple spring-like force towards the web target
      let force = dist * 0.05; // Adjust this value for swing stiffness
      let swingAccelX = cos(angle) * force;
      let swingAccelY = sin(angle) * force;

      // Add gravity
      swingAccelY += GRAVITY;

      // Update swinging velocity
      this.swingVX += swingAccelX;
      this.swingVY += swingAccelY;

      // Apply drag to swinging velocity
      this.swingVX *= 0.98;
      this.swingVY *= 0.98;

      // Update position based on swinging velocity
      this.x += this.swingVX;
      this.y += this.swingVY;

      // Keep Spiderman within canvas bounds
      this.x = constrain(this.x, SPIDERMAN_SIZE / 2, width - SPIDERMAN_SIZE / 2);
      this.y = constrain(this.y, SPIDERMAN_SIZE / 2, height - GROUND_LEVEL - SPIDERMAN_SIZE / 2);

      // Reset regular vertical velocity
      this.vy = 0;
      this.vx = 0; // Reset regular horizontal velocity while swinging
    } else {
      // Normal horizontal movement (using vx)
      if (keyIsDown(LEFT_ARROW)) {
        this.vx = -SPIDERMAN_SPEED;
      } else if (keyIsDown(RIGHT_ARROW)) {
        this.vx = SPIDERMAN_SPEED;
      } else {
        // Apply friction to horizontal movement when no key is pressed
        this.vx *= 0.8; // Adjust friction value
      }

      this.x += this.vx; // Apply horizontal velocity

      // Apply gravity
      this.vy += GRAVITY;
      this.y += this.vy;

      // Prevent falling below ground
      if (this.y > height - GROUND_LEVEL - SPIDERMAN_SIZE / 2) {
        this.y = height - GROUND_LEVEL - SPIDERMAN_SIZE / 2;
        this.vy = 0;
        this.isJumping = false;
        this.vx *= 0.5; // Reduce horizontal velocity on landing
        this.swingVX = 0; // Reset swing velocity when landing
        this.swingVY = 0;
      }

      // Keep Spiderman within canvas bounds
      this.x = constrain(this.x, SPIDERMAN_SIZE / 2, width - SPIDERMAN_SIZE / 2);
    }
  },
Line-by-line explanation (19 lines)

🔧 Subcomponents:

conditional Swinging Physics Branch if (this.isSwinging) {

When swinging, apply spring-like force toward web target plus gravity, update swing velocities, and constrain position

calculation Spring Force Calculation let force = dist * 0.05;

Creates a spring-like force proportional to distance—the farther from the target, the stronger the pull

calculation Swing Drag Application this.swingVX *= 0.98; this.swingVY *= 0.98;

Multiplies swing velocity by 0.98 each frame, gradually slowing the swing (simulating air resistance)

conditional Normal Movement Branch } else {

When not swinging, handle standard keyboard movement with friction and apply gravity

calculation Friction on Idle this.vx *= 0.8;

Reduces horizontal velocity when no key is pressed, creating smooth deceleration

conditional Ground Collision Detection if (this.y > height - GROUND_LEVEL - SPIDERMAN_SIZE / 2) {

Detects when Spider-Man hits the ground and stops his fall, resets jump flag, and applies landing friction

if (this.isSwinging) {
Checks if Spider-Man is currently swinging; if true, use swinging physics instead of normal movement
let dx = this.webTargetX - this.x;
Calculates the horizontal distance from Spider-Man to the web target
let dy = this.webTargetY - this.y;
Calculates the vertical distance from Spider-Man to the web target
let angle = atan2(dy, dx);
Converts dx and dy into an angle pointing from Spider-Man toward the web target—atan2 is the arctangent function that returns angles in radians
let dist = dist(this.x, this.y, this.webTargetX, this.webTargetY);
Calculates the straight-line distance between Spider-Man and the web target—used to determine spring force strength
let force = dist * 0.05;
Creates a spring-like force: if distance is 100 pixels, force = 5; if distance is 50 pixels, force = 2.5—closer means weaker pull (spring relaxation)
let swingAccelX = cos(angle) * force;
Converts the angle and force into an x-component of acceleration using cosine—pulls Spider-Man horizontally toward the target
let swingAccelY = sin(angle) * force;
Converts the angle and force into a y-component of acceleration using sine—pulls Spider-Man vertically toward the target
swingAccelY += GRAVITY;
Adds gravity to the vertical acceleration so Spider-Man doesn't float but swings down like a pendulum
this.swingVX += swingAccelX;
Updates horizontal swing velocity by adding the horizontal acceleration—velocity accumulates over multiple frames
this.swingVX *= 0.98;
Multiplies velocity by 0.98, reducing it by 2% each frame—simulates air drag slowing the swing down
this.x += this.swingVX;
Updates Spider-Man's x position by adding his current swing velocity
if (keyIsDown(LEFT_ARROW)) {
Checks if the left arrow key is currently held down (different from keyPressed, which fires once per press)
this.vx = -SPIDERMAN_SPEED;
Sets horizontal velocity to negative (leftward) when left arrow is held—velocity persists until changed or friction reduces it
this.vx *= 0.8;
Applies friction by multiplying velocity by 0.8 when no key is pressed—reduces velocity by 20% each frame, creating smooth deceleration
if (this.y > height - GROUND_LEVEL - SPIDERMAN_SIZE / 2) {
Checks if Spider-Man's position has fallen past the ground level boundary
this.vy = 0;
Sets vertical velocity to 0 so Spider-Man stops falling and doesn't sink through the ground
this.isJumping = false;
Clears the jumping flag so the player can jump again immediately
this.vx *= 0.5;
Reduces horizontal velocity by half when landing, creating a realistic landing impact that slows horizontal momentum

spiderman.jump()

jump() is a guard function—it prevents bugs by checking preconditions before allowing an action. By setting isJumping = true, it prevents double-jumping even if the player spams the jump button. The jump force is applied instantly as an upward velocity; gravity in move() gradually pulls the velocity back down to 0, creating a natural arc.

jump: function() {
    if (!this.isJumping && !this.isSwinging) {
      this.vy = -SPIDERMAN_JUMP_FORCE;
      this.isJumping = true;
    }
  },
Line-by-line explanation (3 lines)

🔧 Subcomponents:

conditional Jump Validation if (!this.isJumping && !this.isSwinging) {

Prevents double-jumping by checking that Spider-Man is not already jumping and not currently swinging

if (!this.isJumping && !this.isSwinging) {
Uses two ! (NOT) operators to ensure both conditions are false—only jumps if NOT currently jumping AND NOT currently swinging
this.vy = -SPIDERMAN_JUMP_FORCE;
Sets vertical velocity to negative (upward)—negative because y increases downward in p5.js, so negative velocity moves up
this.isJumping = true;
Sets the jumping flag to true, preventing another jump until landing (when move() resets it to false)

spiderman.startSwing(targetX, targetY)

startSwing() transitions Spider-Man from normal movement to web-swinging by setting the isSwinging flag and calculating an initial velocity toward the click point. This initial boost is crucial to game feel—without it, the swing would start slow and feel unresponsive.

startSwing: function(targetX, targetY) {
    this.isSwinging = true;
    this.webTargetX = targetX;
    this.webTargetY = targetY;
    this.isJumping = false;

    // Give an initial boost when swinging starts
    // This makes the swing feel more dynamic
    let dx = targetX - this.x;
    let dy = targetY - this.y;
    let angle = atan2(dy, dx);
    let initialSwingForce = 10; // Adjust for initial swing power
    this.swingVX = cos(angle) * initialSwingForce;
    this.swingVY = sin(angle) * initialSwingForce;
  },
Line-by-line explanation (6 lines)

🔧 Subcomponents:

calculation Initial Swing Boost let initialSwingForce = 10;

Defines the starting velocity magnitude so the swing feels snappy and immediate, not sluggish

this.isSwinging = true;
Sets the swinging flag to true, telling move() to use swinging physics instead of normal movement
this.webTargetX = targetX;
Stores the target x position passed in from the mouse click
this.isJumping = false;
Clears the jumping flag because you can't jump while swinging—they're mutually exclusive
let angle = atan2(dy, dx);
Calculates the angle from Spider-Man to the click point—this determines the initial swing direction
this.swingVX = cos(angle) * initialSwingForce;
Converts angle into an x-component velocity using cosine—gives the swing an immediate boost in the right direction
this.swingVY = sin(angle) * initialSwingForce;
Converts angle into a y-component velocity using sine—the initial push that makes swinging feel responsive

spiderman.endSwing()

endSwing() creates the satisfying feeling of launching from a swing: momentum built during the swing carries into regular velocity, making you fly in the direction you were swinging. This momentum carry-over is crucial for the game to feel good.

endSwing: function() {
    this.isSwinging = false;
    // When web breaks, maintain current swing velocity as new regular velocity
    this.vy = this.swingVY;
    this.vx = this.swingVX; // Carry over horizontal momentum

    // Reset swing velocities
    this.swingVX = 0;
    this.swingVY = 0;
  },
Line-by-line explanation (4 lines)
this.isSwinging = false;
Sets the swinging flag to false, telling move() to return to normal physics on the next frame
this.vy = this.swingVY;
Converts the swing's vertical velocity into regular vertical velocity—if swinging downward at 8 pixels/frame, you'll keep falling at 8 pixels/frame
this.vx = this.swingVX;
Converts the swing's horizontal velocity into regular horizontal velocity—carries momentum from the swing into the jump
this.swingVX = 0;
Resets swing velocity to 0 so it doesn't interfere on the next swing

spiderman.attack(target)

attack() demonstrates two essential game design patterns: cooldowns (preventing spam) and range checks (enforcing game rules). The cooldown uses millis() to track time in real-world milliseconds, which is more robust than using frameCount for frame-based cooldowns. Range checking with dist() is fundamental to most collision and interaction systems.

🔬 This two-level if-statement ensures both cooldown and range are valid before dealing damage. What if you wanted to remove the cooldown so attacks happen every frame? Try deleting the first if-statement so only the range check remains—what happens if you stand next to Deadpool and spam spacebar?

    if (millis() - this.lastAttackTime > this.attackCooldown) {
      if (dist(this.x, this.y, target.x, target.y) < SPIDERMAN_ATTACK_RANGE) {
        target.health -= SPIDERMAN_ATTACK_DAMAGE;
        this.lastAttackTime = millis();
attack: function(target) {
    if (millis() - this.lastAttackTime > this.attackCooldown) {
      if (dist(this.x, this.y, target.x, target.y) < SPIDERMAN_ATTACK_RANGE) {
        target.health -= SPIDERMAN_ATTACK_DAMAGE;
        this.lastAttackTime = millis();
        // Visual feedback for attack
        fill(255, 0, 0, 100);
        noStroke();
        circle(this.x, this.y, SPIDERMAN_ATTACK_RANGE * 2); // Show attack radius
      }
    }
  }
Line-by-line explanation (6 lines)

🔧 Subcomponents:

conditional Cooldown Guard if (millis() - this.lastAttackTime > this.attackCooldown) {

Prevents rapid-fire attacks by checking that enough milliseconds have passed since the last attack

conditional Range Validation if (dist(this.x, this.y, target.x, target.y) < SPIDERMAN_ATTACK_RANGE) {

Ensures the target is close enough to hit—prevents attacking from across the screen

if (millis() - this.lastAttackTime > this.attackCooldown) {
Checks if the time elapsed since the last attack exceeds the cooldown duration (500ms)—millis() returns total milliseconds since the sketch started
if (dist(this.x, this.y, target.x, target.y) < SPIDERMAN_ATTACK_RANGE) {
Uses the dist() function to measure the distance between Spider-Man and the target; only attacks if within range
target.health -= SPIDERMAN_ATTACK_DAMAGE;
Subtracts 10 health from the target (Deadpool in this case)
this.lastAttackTime = millis();
Records the current time, starting the cooldown countdown again—prevents another attack for 500ms
fill(255, 0, 0, 100);
Sets the visual feedback color to red with transparency (alpha = 100)
circle(this.x, this.y, SPIDERMAN_ATTACK_RANGE * 2);
Draws a semi-transparent red circle showing the attack radius, providing visual confirmation that an attack happened

deadpool.move(targetX, targetY)

deadpool.move() shows how simple conditional logic can create convincing AI. By comparing Deadpool's position to Spider-Man's position and moving toward him, the AI feels intentional without complex pathfinding algorithms. The automatic attack makes him a genuine threat without requiring player commands.

🔬 This AI only moves horizontally toward Spider-Man. It could be smarter! What if you added vertical AI logic: if Deadpool is below Spider-Man, make him jump? Try adding after the horizontal logic: } if (this.y > targetY) { this.vy = -10; } to jump toward Spider-Man's height.

    // Simple AI: Move towards Spider-Man
    if (this.x < targetX) {
      this.x += DEADPOOL_SPEED;
    } else if (this.x > targetX) {
      this.x -= DEADPOOL_SPEED;
    }
move: function(targetX, targetY) {
    // Simple AI: Move towards Spider-Man
    if (this.x < targetX) {
      this.x += DEADPOOL_SPEED;
    } else if (this.x > targetX) {
      this.x -= DEADPOOL_SPEED;
    }

    // Apply gravity
    this.vy += GRAVITY;
    this.y += this.vy;

    // Prevent falling below ground
    if (this.y > height - GROUND_LEVEL - DEADPOOL_SIZE / 2) {
      this.y = height - GROUND_LEVEL - DEADPOOL_SIZE / 2;
      this.vy = 0;
    }

    // Keep Deadpool within canvas bounds
    this.x = constrain(this.x, DEADPOOL_SIZE / 2, width - DEADPOOL_SIZE / 2);

    // Basic attack logic
    if (dist(this.x, this.y, targetX, targetY) < DEADPOOL_ATTACK_RANGE) {
      this.attack(spiderman);
    }
  },
Line-by-line explanation (9 lines)

🔧 Subcomponents:

conditional AI Pathfinding if (this.x < targetX) { this.x += DEADPOOL_SPEED; } else if (this.x > targetX) { this.x -= DEADPOOL_SPEED; }

Moves Deadpool left or right toward Spider-Man's current position—simple but effective AI

conditional Automatic Attack if (dist(this.x, this.y, targetX, targetY) < DEADPOOL_ATTACK_RANGE) {

When Deadpool is close enough to Spider-Man, it automatically attacks without waiting for user input

if (this.x < targetX) {
Checks if Deadpool is to the left of Spider-Man (Deadpool's x is smaller)
this.x += DEADPOOL_SPEED;
Moves Deadpool right by adding DEADPOOL_SPEED (2 pixels) to his x position
} else if (this.x > targetX) {
Checks if Deadpool is to the right of Spider-Man (Deadpool's x is larger)
this.x -= DEADPOOL_SPEED;
Moves Deadpool left by subtracting DEADPOOL_SPEED from his x position
this.vy += GRAVITY;
Applies gravity by increasing downward velocity each frame
if (this.y > height - GROUND_LEVEL - DEADPOOL_SIZE / 2) {
Checks if Deadpool has fallen below the ground level
this.vy = 0;
Stops the fall by setting vertical velocity to 0
if (dist(this.x, this.y, targetX, targetY) < DEADPOOL_ATTACK_RANGE) {
Calculates distance to Spider-Man; if within 70 pixels, automatically attacks without player input
this.attack(spiderman);
Calls Deadpool's attack method, dealing damage to Spider-Man if cooldown allows

deadpool.attack(target)

deadpool.attack() mirrors spiderman.attack() but without a range check—Deadpool only attacks when move() calls it after confirming range. This demonstrates how the same attack pattern can be adapted for different characters and game logic.

attack: function(target) {
    if (millis() - this.lastAttackTime > this.attackCooldown) {
      target.health -= DEADPOOL_ATTACK_DAMAGE;
      this.lastAttackTime = millis();
      // Visual feedback for attack
      fill(0, 0, 0, 100);
      noStroke();
      circle(this.x, this.y, DEADPOOL_ATTACK_RANGE * 2); // Show attack radius
    }
  }
Line-by-line explanation (4 lines)

🔧 Subcomponents:

conditional AI Attack Cooldown if (millis() - this.lastAttackTime > this.attackCooldown) {

Prevents Deadpool from attacking every single frame—gives the player time to react with a 1000ms cooldown

if (millis() - this.lastAttackTime > this.attackCooldown) {
Checks if 1000ms (1 second) has passed since Deadpool's last attack—same pattern as Spider-Man's attack cooldown
target.health -= DEADPOOL_ATTACK_DAMAGE;
Subtracts 15 health from the target (Spider-Man)
this.lastAttackTime = millis();
Records the attack time to start the cooldown countdown again
fill(0, 0, 0, 100);
Sets the attack feedback color to black with transparency, matching Deadpool's color scheme (vs. Spider-Man's red)

📦 Key Variables

gameState string

Controls which screen is displayed and what logic runs each frame—values are 'start', 'playing', or 'gameOver'

let gameState = 'start';
SPIDERMAN_SIZE number

The diameter of Spider-Man's circular body in pixels—used for collision detection and display sizing

const SPIDERMAN_SIZE = 50;
SPIDERMAN_SPEED number

How many pixels Spider-Man moves per frame with each arrow key press—controls left/right responsiveness

const SPIDERMAN_SPEED = 4;
SPIDERMAN_JUMP_FORCE number

Initial upward velocity applied when jumping—determines jump height

const SPIDERMAN_JUMP_FORCE = 15;
SPIDERMAN_HEALTH number

Spider-Man's starting health points—game ends when this reaches 0

const SPIDERMAN_HEALTH = 100;
SPIDERMAN_ATTACK_DAMAGE number

Health points removed from an enemy per successful Spider-Man attack

const SPIDERMAN_ATTACK_DAMAGE = 10;
SPIDERMAN_ATTACK_RANGE number

How close Spider-Man must be to a target to deal damage—measured in pixels

const SPIDERMAN_ATTACK_RANGE = 60;
DEADPOOL_SIZE number

The diameter of Deadpool's circular body in pixels

const DEADPOOL_SIZE = 50;
DEADPOOL_SPEED number

How many pixels Deadpool moves per frame toward Spider-Man—controls AI speed

const DEADPOOL_SPEED = 2;
DEADPOOL_HEALTH number

Deadpool's starting health points—beating the game requires reducing this to 0

const DEADPOOL_HEALTH = 120;
DEADPOOL_ATTACK_DAMAGE number

Health points removed from Spider-Man per successful Deadpool attack

const DEADPOOL_ATTACK_DAMAGE = 15;
DEADPOOL_ATTACK_RANGE number

How close Deadpool must be to Spider-Man to attack automatically—measured in pixels

const DEADPOOL_ATTACK_RANGE = 70;
GRAVITY number

Acceleration applied to characters each frame—simulates Earth's gravity pulling them downward

const GRAVITY = 0.6;
GROUND_LEVEL number

The height of the ground area from the bottom of the canvas—characters stand on this level

const GROUND_LEVEL = 50;
spiderman object

Stores all Spider-Man's properties (position, health, velocities) and methods (move, jump, attack, display, swing)

let spiderman = { x: 100, y: 0, health: 100, ... };
deadpool object

Stores all Deadpool's properties (position, health, velocity) and methods (move, attack, display)

let deadpool = { x: 0, y: 0, health: 120, ... };

🔧 Potential Improvements (6)

Here are some ways this code could be enhanced:

BUG spiderman.move() - swinging physics

Using dist() as both a variable name and a function call in the same scope causes a collision: 'let dist = dist(...)' shadows the function

💡 Rename the distance variable to something like 'distToTarget' to avoid shadowing the p5.js dist() function

PERFORMANCE drawCityBackground()

The building background is regenerated from scratch every frame using random(), making the skyline flicker and change constantly

💡 Generate the skyline once in setup() and store it in an array, then draw the cached buildings each frame for a stable, faster-rendering background

STYLE spiderman object

The attack() method draws visual feedback (the red circle) inside the physics logic, violating separation of concerns—display code shouldn't live in game logic

💡 Remove the fill/circle drawing from attack() and instead set a flag like 'justAttacked = true'; then have display() render the visual feedback if this flag is set

FEATURE deadpool AI

Deadpool only moves horizontally toward Spider-Man and never jumps, making him predictable and easy to evade by jumping overhead

💡 Add vertical AI: if Spider-Man is higher than Deadpool by more than a threshold (e.g., 50 pixels), make Deadpool jump toward Spider-Man's height to close the gap

BUG spiderman.move() - ground collision

Characters can get stuck in the ground if velocity is very high on landing, causing clipping and unpredictable behavior

💡 Add a minimum position clamp: ensure y is never set below the ground level even when velocities are extreme

FEATURE Game state management

There is no pause feature—once the game starts, it runs continuously until someone dies

💡 Add a 'paused' gameState and allow pressing 'P' key to toggle pause, displaying 'PAUSED' text and freezing all character updates

🔄 Code Flow

Code flow showing setup, draw, keypressed, mousepressed, mousereleased, windowresized, resetgame, displaystartscreen, displaygameoverscreen, displayhealthbar, drawcitybackground, drawground, spidermanmove, spidermanjump, spidermanstartswing, spidermanendswing, spidermanattack, deadpoolmove, deadpoolattack

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

graph TD start[Start] --> setup[setup] setup --> resetgame[resetGame] resetgame --> draw[draw loop] draw --> game-state-start[game-state-start] game-state-start --> click-restart[click-restart] game-state-start --> restart-control[restart-control] draw --> game-state-playing[game-state-playing] game-state-playing --> health-check[health-check] health-check --> game-state-gameover[game-state-gameover] game-state-playing --> playing-controls[playing-controls] playing-controls --> jump-control[jump-control] playing-controls --> attack-control[attack-control] draw --> game-state-gameover game-state-gameover --> click-restart draw --> canvas-resize[canvas-resize] draw --> drawcitybackground[drawCityBackground] draw --> drawground[drawGround] draw --> spidermanmove[spiderman.move] spidermanmove --> normal-movement[normal-movement] normal-movement --> friction[friction] normal-movement --> ground-collision[ground-collision] ground-collision --> jump-guard[jump-guard] spidermanmove --> swing-physics[swing-physics] swing-physics --> spring-force[spring-force] swing-physics --> swing-drag[swing-drag] spidermanmove --> spidermanstartswing[spiderman.startSwing] spidermanmove --> spidermanendswing[spiderman.endSwing] spidermanmove --> spidermanattack[spiderman.attack] spidermanattack --> cooldown-check[cooldown-check] spidermanattack --> range-check[range-check] draw --> deadpoolmove[deadpool.move] deadpoolmove --> ai-logic[ai-logic] ai-logic --> auto-attack[auto-attack] auto-attack --> ai-cooldown[ai-cooldown] click setup href "#fn-setup" click resetgame href "#fn-resetgame" click draw href "#fn-draw" click game-state-start href "#sub-game-state-start" click click-restart href "#sub-click-restart" click restart-control href "#sub-restart-control" click game-state-playing href "#sub-game-state-playing" click health-check href "#sub-health-check" click game-state-gameover href "#sub-game-state-gameover" click playing-controls href "#sub-playing-controls" click jump-control href "#sub-jump-control" click attack-control href "#sub-attack-control" click canvas-resize href "#sub-canvas-resize" click drawcitybackground href "#fn-drawcitybackground" click drawground href "#fn-drawground" click spidermanmove href "#fn-spidermanmove" click normal-movement href "#sub-normal-movement" click friction href "#sub-friction" click ground-collision href "#sub-ground-collision" click jump-guard href "#sub-jump-guard" click swing-physics href "#sub-swing-physics" click spring-force href "#sub-spring-force" click swing-drag href "#sub-swing-drag" click spidermanstartswing href "#fn-spidermanstartswing" click spidermanendswing href "#fn-spidermanendswing" click spidermanattack href "#fn-spidermanattack" click cooldown-check href "#sub-cooldown-check" click range-check href "#sub-range-check" click deadpoolmove href "#fn-deadpoolmove" click ai-logic href "#sub-ai-logic" click auto-attack href "#sub-auto-attack" click ai-cooldown href "#sub-ai-cooldown"

❓ Frequently Asked Questions

What visual elements are featured in the p5.js sketch titled 'Sketch 2026-02-22 22:27'?

The sketch visually represents Spider-Man as a simplified character, depicted with a red body, white eyes, and a blue spider symbol, set against a dynamic background.

How can users interact with the Spider-Man character in this creative coding sketch?

Users can control Spider-Man's movements and actions, such as jumping and attacking, through keyboard inputs, enhancing the interactive gameplay experience.

What creative coding concepts does this p5.js sketch showcase?

This sketch demonstrates concepts like object-oriented programming, character animation, and basic physics for movement, showcasing how to create interactive game elements.

Preview

Sketch 2026-02-22 22:27 - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of Sketch 2026-02-22 22:27 - Code flow showing setup, draw, keypressed, mousepressed, mousereleased, windowresized, resetgame, displaystartscreen, displaygameoverscreen, displayhealthbar, drawcitybackground, drawground, spidermanmove, spidermanjump, spidermanstartswing, spidermanendswing, spidermanattack, deadpoolmove, deadpoolattack
Code Flow Diagram