five night at diddys

This sketch recreates a simplified version of Five Nights at Freddy's in 3D using three.js for 3D rendering and p5.js for UI overlays. Players manage power, close doors, turn on lights, and survive animatronic attacks by monitoring security cameras and controlling office defenses for six in-game minutes.

🧪 Try This!

Experiment with the code by making these changes:

  1. Speed up the animatronics — Make Freddy and Bonnie move faster by reducing their initial move times, creating a harder game with less time to react.
  2. Make doors more expensive — Increase the power drain when doors are closed, forcing you to balance security with power conservation.
  3. Give more time to survive — Increase the game duration from 6 minutes to 10 minutes, giving you more time to manage resources.
  4. Make cameras drain less power — Reduce the camera drain rate so you can use them more frequently for longer without losing power.
  5. Change the background color — Alter the 3D scene's atmosphere by changing the background from black to a different color like dark blue.
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch creates a playable survival horror game inspired by Five Nights at Freddy's. Players sit in an office, watching security cameras to track animatronic movement, while managing limited power to operate doors, lights, and camera systems. The game teaches three powerful p5.js and three.js techniques: managing complex game state across multiple screens (title, playing, game over), blending 3D graphics from three.js with 2D UI overlays from p5.js, and implementing AI-driven enemy movement through state machines.

The code is organized into distinct sections: game variables tracking power and time, animatronic position and movement logic, a three.js 3D scene with fully modeled characters and environments, and a p5.js overlay system for buttons and HUD display. By studying it, you will learn how to combine two graphics libraries, implement procedural game AI, design interactive UI systems, and structure a game loop that updates multiple systems in sync.

⚙️ How It Works

  1. When the sketch loads, setup() initializes both the p5.js canvas and a three.js 3D scene, creating the office environment, two animatronics (Freddy and Bonnie), and four security cameras. The three.js renderer runs on a hidden canvas layer beneath the p5.js canvas.
  2. The draw loop runs 60 times per second, updating power, time, and animatronic movement logic, then rendering the 3D scene and drawing 2D UI elements on top. The game state variable ('title', 'playing', 'gameover', 'win', 'power_out') determines which screen and logic runs each frame.
  3. Animatronics move through predictable locations (stage → hall → door → office) on random timers. When an animatronic reaches the office or power runs out, a jumpscare triggers and the game ends.
  4. The player toggles doors and lights with mouse clicks, which drain power at different rates. Closing a door blocks animatronic entry; turning on a light reveals threats at that door via a p5.js overlay.
  5. Surviving until time runs out (six in-game minutes = 360 real-time seconds) wins the game. Running out of power causes a power-out state, leading to an unavoidable jumpscare and game over.
  6. Pressing SPACE switches between office view (showing 3D scene + UI buttons) and camera view (showing security feeds with green static overlay). The C key or RIGHT ARROW cycles between camera locations.

🎓 Concepts You'll Learn

Game state managementFinite state machines for AIThree.js 3D scene setup and renderingp5.js UI overlay systemDelta time-based animationEvent handling and user interactionPower and resource management

📝 Code Breakdown

setup()

setup() runs once when the sketch starts. Use it to initialize your canvas, set default styles, and call any scene initialization functions. In this game, setup() prepares both the p5.js UI layer and the three.js 3D graphics layer.

function setup() {
  // Create p5.js canvas for 2D UI overlay
  createCanvas(800, 600);
  frameRate(60);
  textFont("Arial");
  textAlign(CENTER, CENTER);

  // Initialize three.js scene and objects
  setup3D();

  // User must interact to start audio
  userStartAudio(); // Required for p5.sound to work in modern browsers
}
Line-by-line explanation (6 lines)

🔧 Subcomponents:

function-call Canvas and text initialization createCanvas(800, 600);

Creates an 800x600 pixel p5.js canvas that will sit on top of the three.js renderer

function-call Three.js scene initialization setup3D();

Calls the setup3D function to initialize the three.js scene, cameras, lights, and 3D objects

createCanvas(800, 600);
Creates an 800×600 pixel canvas managed by p5.js. This canvas will float above the three.js rendering canvas to draw UI elements.
frameRate(60);
Locks the frame rate to 60 FPS, ensuring consistent game updates and smooth animation.
textFont("Arial");
Sets the font for all text drawn by p5.js to Arial, keeping the UI consistent.
textAlign(CENTER, CENTER);
Centers all text horizontally and vertically by default, making it easier to position UI elements.
setup3D();
Calls the setup3D() function, which initializes the three.js scene, renderer, cameras, lights, and all 3D objects (office, doors, lights, animatronics).
userStartAudio();
A p5.sound function required by modern browsers—allows audio to play only after user interaction, satisfying browser security policies.

setup3D()

setup3D() runs once during setup() and initializes the entire three.js graphics engine. It creates the scene, renderer, cameras, lights, and all 3D objects (office, animatronics, doors). Understanding this function teaches you how three.js applications are structured: create a scene, set up a renderer, position cameras and lights, then add geometry.

🔬 This ambient light is set to 0x404040, a medium gray. What happens if you change it to 0x000000 (black) or 0xffffff (white)? Try one and see how the scene's brightness changes.

  const ambientLight = new THREE.AmbientLight(0x404040); // Soft ambient light
  scene.add(ambientLight);
function setup3D() {
  scene = new THREE.Scene();
  scene.background = new THREE.Color(0x000000); // Black background for 3D scene

  // Setup Renderer
  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(width, height);
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.shadowMap.enabled = true; // Enable shadows
  renderer.shadowMap.type = THREE.PCFSoftShadowMap; // Softer shadows

  // Append three.js renderer to the p5.js canvas container
  // This allows p5.js to draw on top later
  document.getElementById('defaultCanvas0').parentNode.appendChild(renderer.domElement);
  renderer.domElement.style.position = 'absolute';
  renderer.domElement.style.top = '0px';
  renderer.domElement.style.left = '0px';
  renderer.domElement.style.zIndex = '-1'; // Ensure p5.js canvas draws on top

  // Setup Cameras
  officeCamera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
  officeCamera.position.set(0, 1.7, 5); // Inside the office, looking forward
  officeCamera.lookAt(0, 1.7, 0); // Look towards the center of the room

  cam01Camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
  cam01Camera.position.set(0, 2, -10); // Outside the office, looking at the main hallway
  cam01Camera.lookAt(0, 2, 0); // Look towards the office doors

  cam02Camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
  cam02Camera.position.set(0, 2, -20); // Stage cam, looking at the stage area
  cam02Camera.lookAt(0, 2, -25); // Look towards the stage

  // Store cameras in an array for cycling
  cameraLocations = [
    { name: "CAM 01: MAIN HALLWAY", camera: cam01Camera },
    { name: "CAM 02: STAGE", camera: cam02Camera },
    // Add more camera locations here:
    // { name: "CAM 03: DINING AREA", camera: diningCamera },
  ];

  // Add Lights
  const ambientLight = new THREE.AmbientLight(0x404040); // Soft ambient light
  scene.add(ambientLight);

  const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
  directionalLight.position.set(5, 10, 5);
  directionalLight.castShadow = true;
  directionalLight.shadow.mapSize.width = 1024;
  directionalLight.shadow.mapSize.height = 1024;
  directionalLight.shadow.camera.near = 0.5;
  directionalLight.shadow.camera.far = 50;
  directionalLight.shadow.camera.left = -10;
  directionalLight.shadow.camera.right = 10;
  directionalLight.shadow.camera.top = 10;
  directionalLight.shadow.camera.bottom = -10;
  scene.add(directionalLight);

  // --- Create Office Environment ---
  createOffice();
  createDoorsAndLights();
  createStageArea(); // Call to create the stage

  // --- Create Animatronic Meshes ---
  freddyMesh = createFreddyMesh();
  scene.add(freddyMesh);
  // Initially position Freddy on the stage
  freddyMesh.position.set(-2, 0, -25);
  freddyMesh.rotation.y = Math.PI; // Face the camera
  freddyMesh.visible = true; // Initially visible on stage

  bonnieMesh = createBonnieMesh();
  scene.add(bonnieMesh);
  // Initially position Bonnie on the stage
  bonnieMesh.position.set(2, 0, -25);
  bonnieMesh.rotation.y = Math.PI; // Face the camera
  bonnieMesh.visible = true; // Initially visible on stage

  jumpscareFreddyMesh = createJumpscareFreddyMesh();
  scene.add(jumpscareFreddyMesh);
  jumpscareFreddyMesh.visible = false; // Hide until jumpscare
}
Line-by-line explanation (17 lines)

🔧 Subcomponents:

calculation Three.js scene initialization scene = new THREE.Scene();

Creates the 3D scene that will hold all 3D objects, lights, and cameras

calculation WebGL renderer configuration renderer = new THREE.WebGLRenderer({ antialias: true });

Creates the three.js renderer using WebGL, which will draw 3D graphics to a canvas

calculation Camera locations array cameraLocations = [

Stores camera objects in an array so the player can cycle between different security camera views

calculation Scene lighting const ambientLight = new THREE.AmbientLight(0x404040);

Creates ambient and directional lights to illuminate the 3D scene and cast shadows

function-call Environment and character creation createOffice();

Calls helper functions to create the office, doors, lights, stage, and animatronic meshes

scene = new THREE.Scene();
Creates a new three.js Scene object—the container that holds all 3D geometry, lights, and cameras.
scene.background = new THREE.Color(0x000000);
Sets the scene's background color to black (hex 0x000000), creating a dark atmosphere for the horror game.
renderer = new THREE.WebGLRenderer({ antialias: true });
Creates the WebGL renderer that will draw the 3D scene. The { antialias: true } option smooths edges for better visual quality.
renderer.setSize(width, height);
Sizes the three.js renderer canvas to match the p5.js canvas dimensions (800×600).
renderer.shadowMap.enabled = true;
Enables shadow rendering in the scene, allowing 3D objects to cast shadows on each other for depth and realism.
document.getElementById('defaultCanvas0').parentNode.appendChild(renderer.domElement);
Appends the three.js renderer's canvas to the same HTML container as p5.js, layering them together.
renderer.domElement.style.zIndex = '-1';
Sets the three.js canvas to render behind the p5.js canvas (negative z-index), so UI buttons and text draw on top of the 3D scene.
officeCamera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
Creates a camera inside the office with a 75-degree field of view, positioned at player height (y = 1.7) looking forward into the room.
cam01Camera.position.set(0, 2, -10);
Positions the main hallway camera at a distance from the office, simulating a security camera's viewpoint of the hallway.
cam02Camera.position.set(0, 2, -20);
Positions the stage camera even farther back, showing the performance stage where Freddy and Bonnie start the game.
cameraLocations = [
Stores all security cameras in an array with their names, allowing the player to cycle between views with the C key.
const ambientLight = new THREE.AmbientLight(0x404040);
Creates soft background lighting that illuminates all objects evenly, preventing complete darkness in shadowed areas.
directionalLight.castShadow = true;
Enables shadow casting for the directional light, allowing objects to cast shadows on the floor and walls.
createOffice();
Calls the createOffice() function to generate the office floor, ceiling, and walls.
freddyMesh = createFreddyMesh();
Creates the Freddy animatronic mesh by calling createFreddyMesh(), which returns a three.js Group containing all his body parts.
freddyMesh.position.set(-2, 0, -25);
Positions Freddy on the stage at the left side, 25 units away from the office (deep in the scene).
jumpscareFreddyMesh.visible = false;
Hides the jumpscare version of Freddy until the moment a jumpscare is triggered, at which point it will flash on screen.

draw()

draw() runs every frame (60 times per second) and orchestrates the entire game loop. It updates the 3D scene, renders it, clears the p5.js canvas, and draws the appropriate UI based on game state. This is the heartbeat of the game—everything that moves or changes happens because draw() calls the right update and drawing functions each frame.

function draw() {
  // Update three.js scene based on game state
  update3DScene();

  // Draw three.js scene (this renders to the three.js renderer's canvas)
  draw3D();

  // Clear p5.js canvas (makes it transparent so three.js scene shows through)
  clear();

  // Draw p5.js UI overlay
  switch (gameState) {
    case 'title':
      drawTitleScreen();
      break;
    case 'playing':
      drawGameUI(); // Only draw UI, 3D scene is drawn by draw3D()
      break;
    case 'gameover':
      drawGameOverScreen();
      break;
    case 'win':
      drawWinScreen();
      break;
    case 'power_out':
      drawPowerOutScreen();
      break;
  }
}
Line-by-line explanation (5 lines)

🔧 Subcomponents:

function-call Update 3D scene state update3DScene();

Updates the visibility and position of 3D objects based on current game state (doors, lights, animatronics, cameras)

function-call Render 3D graphics draw3D();

Calls the three.js renderer to draw the 3D scene to its canvas

function-call Clear p5.js canvas clear();

Clears the p5.js canvas, making it transparent so the three.js scene shows through while the p5.js UI draws on top

switch-case Game state dispatcher switch (gameState) {

Routes to the correct drawing function based on current game state (title, playing, gameover, win, or power_out)

update3DScene();
Calls update3DScene() to update the visibility and positions of 3D objects (doors, lights, animatronics) based on the current game variables.
draw3D();
Calls draw3D(), which renders the three.js scene to the three.js canvas using the current camera.
clear();
Clears the p5.js canvas to transparency, allowing the three.js rendering behind it to show through. The p5.js canvas is now ready for UI drawing.
switch (gameState) {
Examines the current gameState variable and routes to the appropriate drawing function (drawTitleScreen, drawGameUI, etc.).
case 'playing':
If gameState is 'playing', the next line calls drawGameUI(), which updates power, time, animatronic movement, and draws buttons and HUD.

update3DScene()

update3DScene() runs every frame before rendering. It synchronizes the 3D world with the game variables: if a door is closed, the mesh becomes visible; if a light is on, the light becomes visible and bright; if an animatronic moves to a new location, this function updates their position. This is where game logic affects 3D graphics.

🔬 These lines use the ! operator to invert the boolean. What happens if you remove the ! symbols so it reads leftDoorMesh.visible = leftDoorOpen instead? Now the door appears when it's open instead of when it's closed—try it and see the logic flip.

  // Update door visibility
  leftDoorMesh.visible = !leftDoorOpen;
  rightDoorMesh.visible = !rightDoorOpen;
function update3DScene() {
  // Update door visibility
  leftDoorMesh.visible = !leftDoorOpen;
  rightDoorMesh.visible = !rightDoorOpen;

  // Update light visibility (cone visualizer + actual PointLight)
  leftLightMesh.visible = leftLightOn;
  rightLightMesh.visible = rightLightOn;
  leftDoorPointLight.visible = leftLightOn;
  rightDoorPointLight.visible = rightLightOn;


  // Set currentCamera based on camActive
  if (!camActive) {
    currentCamera = officeCamera;
  } else {
    currentCamera = cameraLocations[currentCamIndex].camera;
  }

  // Update Freddy's 3D visibility and position
  if (gameState !== 'playing' || currentCamera === officeCamera) {
    freddyMesh.visible = false;
  } else if (camActive) {
    switch (freddyLocation) {
      case 'stage':
        freddyMesh.visible = true; // Visible on ALL cams if in stage location
        freddyMesh.position.set(-2, 0, -25); // Set stage position
        freddyMesh.rotation.y = Math.PI; // Face the camera
        break;
      case 'hall':
        freddyMesh.visible = true; // Visible on ALL cams if in hall location
        freddyMesh.position.set(0, 0, -10); // Set hall position
        freddyMesh.rotation.y = Math.PI; // Face the camera
        break;
      case 'office_left_door':
        freddyMesh.visible = false; // Rendered by p5.js overlay if light is on
        break;
      case 'office':
        freddyMesh.visible = false; // Hidden just before jumpscare
        break;
      default:
        freddyMesh.visible = false;
    }
  }

  // Update Bonnie's 3D visibility and position
  if (gameState !== 'playing' || currentCamera === officeCamera) {
    bonnieMesh.visible = false;
  } else if (camActive) {
    switch (bonnieLocation) {
      case 'stage':
        bonnieMesh.visible = true; // Visible on ALL cams if in stage location
        bonnieMesh.position.set(2, 0, -25); // Set stage position
        bonnieMesh.rotation.y = Math.PI; // Face the camera
        break;
      case 'hall':
        bonnieMesh.visible = true; // Visible on ALL cams if in hall location
        bonnieMesh.position.set(0, 0, -10); // Set hall position
        bonnieMesh.rotation.y = Math.PI; // Face the camera
        break;
      case 'office_right_door':
        bonnieMesh.visible = false; // Rendered by p5.js overlay if light is on
        break;
      case 'office':
        bonnieMesh.visible = false; // Hidden just before jumpscare
        break;
      default:
        bonnieMesh.visible = false;
    }
  }

  // Update jumpscare visibility (only active during actual jumpscare moment)
  jumpscareFreddyMesh.visible = false; // Default to hidden
}
Line-by-line explanation (10 lines)

🔧 Subcomponents:

calculation Door mesh visibility toggle leftDoorMesh.visible = !leftDoorOpen;

Shows the door mesh only when the door is closed; when the door is open, the mesh is invisible so you can see through the opening

calculation Light cone and point light toggle leftLightMesh.visible = leftLightOn;

Shows the yellow light cone visualizer and enables the point light whenever the light is turned on

conditional Camera selection if (!camActive) { currentCamera = officeCamera; } else { currentCamera = cameraLocations[currentCamIndex].camera; }

Chooses which camera to use for rendering—office view or the currently selected security camera

switch-case Freddy position and visibility update switch (freddyLocation) {

Updates Freddy's 3D position, rotation, and visibility based on his current location (stage, hall, door, office)

switch-case Bonnie position and visibility update switch (bonnieLocation) {

Updates Bonnie's 3D position, rotation, and visibility based on her current location

leftDoorMesh.visible = !leftDoorOpen;
The ! operator inverts the boolean: if leftDoorOpen is true, the door mesh becomes invisible (you see through the opening). If leftDoorOpen is false, the mesh becomes visible (the door blocks the opening).
leftLightMesh.visible = leftLightOn;
Shows the light cone visualizer whenever leftLightOn is true, allowing the player to see the yellow cone from the door light.
if (!camActive) {
If the player is NOT in camera mode, use the office camera (you see the 3D office scene). The ! operator inverts the camActive boolean.
currentCamera = cameraLocations[currentCamIndex].camera;
If the player IS in camera mode, use the security camera at the current index, cycling through CAM 01 and CAM 02.
if (gameState !== 'playing' || currentCamera === officeCamera) {
Hide Freddy if the game is not playing OR if the office camera is active. The || operator means 'or'—either condition hides him.
case 'stage':
If Freddy is on the stage, make him visible and position him at the stage location (-2, 0, -25) facing toward the camera.
case 'hall':
If Freddy is in the hallway, make him visible and position him at the hallway location (0, 0, -10), closer to the office.
case 'office_left_door':
If Freddy is at the left door, hide the 3D mesh because a p5.js overlay will draw him there when the light is on.
freddyMesh.position.set(-2, 0, -25);
Moves Freddy to the stage position: X = -2 (left side), Y = 0 (ground level), Z = -25 (deep in the scene).
freddyMesh.rotation.y = Math.PI;
Rotates Freddy 180 degrees around his Y axis (vertical spin), making him face toward the camera—Math.PI radians = 180 degrees.

drawGameUI()

drawGameUI() is the game's main logic loop during gameplay. It calculates power drain based on active systems, updates timers, moves animatronics, checks win/lose conditions, and draws the appropriate UI. This function is where most game rules and balance happen—adjust these numbers to make the game easier or harder.

🔬 This code stacks up power consumption—each active system adds to the drain rate. What happens if you comment out the camera line? Now using the camera won't drain power at all. Try it and see how much longer you can survive.

  let drainRate = 0.05; // Base power drain per second
  if (camActive) drainRate += 0.15; // Camera active
  if (!leftDoorOpen) drainRate += 0.1; // Left door closed
  if (!rightDoorOpen) drainRate += 0.1; // Right door closed
  if (leftLightOn) drainRate += 0.05; // Left light on
  if (rightLightOn) drainRate += 0.05; // Right light on
function drawGameUI() {
  // This function draws the p5.js UI elements on top of the 3D scene.
  // The 3D scene itself is rendered by draw3D().

  // --- 2. Power & Time Logic ---
  let drainRate = 0.05; // Base power drain per second
  if (camActive) drainRate += 0.15; // Camera active
  if (!leftDoorOpen) drainRate += 0.1; // Left door closed
  if (!rightDoorOpen) drainRate += 0.1; // Right door closed
  if (leftLightOn) drainRate += 0.05; // Left light on
  if (rightLightOn) drainRate += 0.05; // Right light on

  power -= drainRate * (deltaTime / 1000);
  timeRemaining -= deltaTime / 1000;
  freddyMoveTimer += deltaTime / 1000;
  bonnieMoveTimer += deltaTime / 1000;

  if (power <= 0) {
    console.log("OUT OF POWER!");
    gameState = 'power_out';
  }
  if (timeRemaining <= 0) {
    console.log("YOU SURVIVED UNTIL 6 AM!");
    gameState = 'win';
  }

  // --- Animatronic Movement & Logic ---
  checkFreddyMove();
  checkBonnieMove();
  checkJumpScare(); // Check if any animatronic is in the office

  // --- 3. Drawing the UI ---
  if (!camActive) {
    // Office View UI (buttons overlaid on 3D scene)
    drawDoorsButtons();
    drawLightsButtons();
  } else {
    // Camera Overlay UI
    drawCameraOverlayUI();
  }

  // --- Stats HUD (Power and Time) ---
  drawHUD();

  // --- Debug HUD (Animatronic Status) ---
  drawDebugHUD();
}
Line-by-line explanation (11 lines)

🔧 Subcomponents:

calculation Power drain calculation let drainRate = 0.05;

Calculates how much power is lost per second based on active systems (camera, doors, lights)

calculation Power and timer updates power -= drainRate * (deltaTime / 1000);

Decreases power based on elapsed time and drain rate; updates timers for Freddy and Bonnie movement

conditional Win/lose condition checks if (power <= 0) {

Checks if the player has run out of power or survived until 6 AM, triggering game-over or win states

function-call Animatronic movement logic checkFreddyMove();

Calls functions to move Freddy and Bonnie according to their timers and state machines

conditional UI drawing dispatcher if (!camActive) {

Draws either office buttons (when not on camera) or camera overlay (when on camera)

let drainRate = 0.05; // Base power drain per second
Initializes drainRate to 0.05—a baseline power loss. Each active system will add to this rate.
if (camActive) drainRate += 0.15; // Camera active
If the camera is active, add 0.15 to the drain rate. The camera is power-hungry, draining significantly when used.
if (!leftDoorOpen) drainRate += 0.1; // Left door closed
If the left door is CLOSED, add 0.1 to drain. Keeping doors closed requires constant power to hold them shut.
power -= drainRate * (deltaTime / 1000);
Reduces the power variable by the drain rate multiplied by the elapsed time in seconds. deltaTime is in milliseconds, so dividing by 1000 converts to seconds.
timeRemaining -= deltaTime / 1000;
Decreases the time remaining by the elapsed time in seconds. When timeRemaining reaches 0, the player wins.
freddyMoveTimer += deltaTime / 1000;
Increments Freddy's move timer by elapsed seconds. When this timer exceeds freddyMoveTime, Freddy moves to the next location.
if (power <= 0) {
If power runs out, change the game state to 'power_out', triggering a loss condition and eventual jumpscare.
if (timeRemaining <= 0) {
If time remaining reaches 0 (6 AM has arrived), change game state to 'win' and the player survives.
checkFreddyMove();
Calls the checkFreddyMove() function, which checks if Freddy's move timer has expired and moves him to a new location if so.
if (!camActive) {
If the player is NOT in camera mode (i.e., viewing the office), draw the door and light buttons. The ! operator inverts camActive.
drawCameraOverlayUI();
If the player IS in camera mode, draw the camera overlay (green screen with static effect) instead of buttons.

checkFreddyMove()

checkFreddyMove() implements Freddy's AI using a finite state machine. His behavior depends on his current location (stage, hall, door, office). Each location has specific rules for where he moves next and how long he waits. This is the pattern used in most game AI—define states and the rules for transitioning between them.

🔬 This code gives Freddy two choices when in the hall: 50% chance to approach the door, 50% chance to wait. What happens if you add an 'else if' for a 25% chance to retreat back to the stage? You'd need to add another condition and set freddyLocation back to 'stage'.

    case 'hall':
      // 50% chance to move to office_left_door, 50% to stay or move back
      if (random() < 0.5) {
        freddyLocation = 'office_left_door';
        freddyMoveTime = random(10, 30); // Next move faster approaching door
        freddyAtDoorTimer = 0; // Reset door timer
      } else {
        freddyMoveTime = random(60, 120); // Wait longer in hall
      }
function checkFreddyMove() {
  if (gameState !== 'playing' || !freddyActive || freddyMoveTimer < freddyMoveTime) return;

  // Decide Freddy's next location
  switch (freddyLocation) {
    case 'stage':
      freddyLocation = 'hall';
      freddyMoveTime = random(40, 80); // Next move in 40-80 seconds
      break;
    case 'hall':
      // 50% chance to move to office_left_door, 50% to stay or move back
      if (random() < 0.5) {
        freddyLocation = 'office_left_door';
        freddyMoveTime = random(10, 30); // Next move faster approaching door
        freddyAtDoorTimer = 0; // Reset door timer
      } else {
        freddyMoveTime = random(60, 120); // Wait longer in hall
      }
      break;
    case 'office_left_door':
      // If door is open, start timer to enter
      if (leftDoorOpen) {
        freddyAtDoorTimer += deltaTime / 1000;
        if (freddyAtDoorTimer >= freddyAtDoorDelay) {
          freddyLocation = 'office'; // Enter office
          freddyMoveTime = random(2, 5); // Very short time before jumpscare in office
        }
      } else {
        // Door is closed, Freddy is blocked. Reset timer and wait.
        freddyAtDoorTimer = 0;
        freddyMoveTime = random(20, 60); // Freddy backs off and tries again later
      }
      break;
    case 'office':
      // If Freddy is in the office, the game should be over soon
      freddyMoveTime = random(2, 5); // Very short time before jumpscare
      break;
  }
  freddyMoveTimer = 0; // Reset timer
  console.log(`Freddy moved to: ${freddyLocation}`);
}
Line-by-line explanation (9 lines)

🔧 Subcomponents:

conditional Early function return if (gameState !== 'playing' || !freddyActive || freddyMoveTimer < freddyMoveTime) return;

Exits the function immediately if the game isn't playing, Freddy is inactive, or his timer hasn't expired yet

switch-case Freddy location state machine switch (freddyLocation) {

Routes Freddy's movement logic based on his current location, implementing a simple AI that progresses from stage → hall → door → office

conditional Door entry timer if (leftDoorOpen) {

If the door is open, increment the timer; when it expires, Freddy enters the office and triggers a jumpscare

if (gameState !== 'playing' || !freddyActive || freddyMoveTimer < freddyMoveTime) return;
Three conditions must be met to proceed: the game must be playing, Freddy must be active, and his move timer must have exceeded the move time. If ANY of these fail (using ||, which means 'or'), the function exits immediately. This is an optimization—don't process movement if it's not time yet.
case 'stage':
If Freddy is on the stage, always move him to the hallway next. Set a random time of 40-80 seconds before the next move.
if (random() < 0.5) {
random() returns a number between 0 and 1. If it's less than 0.5 (50% chance), move to the door. Otherwise, stay in the hall.
freddyLocation = 'office_left_door';
Moves Freddy to the left door of the office. He will now wait there and try to enter if the door is open.
if (leftDoorOpen) {
If the left door is currently open, Freddy can enter. Increment his door timer; when it reaches the delay, he enters the office.
freddyAtDoorTimer += deltaTime / 1000;
Increments the door timer by the elapsed time in seconds. This creates a delay between when Freddy arrives at the door and when he actually enters.
if (freddyAtDoorTimer >= freddyAtDoorDelay) {
If Freddy has waited at the door long enough (3 seconds by default), move him into the office—this will trigger a jumpscare soon.
} else {
If the door is CLOSED, Freddy cannot enter. Reset his door timer and make him wait 20-60 seconds before trying again.
freddyMoveTimer = 0; // Reset timer
After Freddy moves to his new location, reset his move timer back to 0. He will now wait until the timer exceeds the new moveTime before moving again.

checkJumpScare()

checkJumpScare() is a simple but crucial function—it's the final trigger for game over. Both animatronics have their own timers and positions; if either reaches the office and their timer expires, the game ends with a jumpscare. This function checks both conditions every frame.

function checkJumpScare() {
  // If Freddy or Bonnie is in the office and their timer runs out, it's a jump scare!
  if ((freddyLocation === 'office' && freddyMoveTimer >= freddyMoveTime) ||
      (bonnieLocation === 'office' && bonnieMoveTimer >= bonnieMoveTime)) {
    doJumpScare();
    // gameState will be set to 'gameover' by doJumpScare after its delay
  }
}
Line-by-line explanation (3 lines)

🔧 Subcomponents:

conditional Jumpscare condition check if ((freddyLocation === 'office' && freddyMoveTimer >= freddyMoveTime) ||

Checks if either Freddy or Bonnie has entered the office and their jumpscare timer has expired, triggering an instant game-over

if ((freddyLocation === 'office' && freddyMoveTimer >= freddyMoveTime) ||
First condition: Freddy is in the office AND his timer has exceeded the jump-scare time. The && operator means both must be true.
(bonnieLocation === 'office' && bonnieMoveTimer >= bonnieMoveTime)) {
Second condition: Bonnie is in the office AND her timer has exceeded the jump-scare time. The || operator means either condition triggers the jumpscare.
doJumpScare();
If either animatronic reaches the end of their timer in the office, call doJumpScare() to trigger the jumpscare sequence and game over.

doJumpScare()

doJumpScare() is called when an animatronic reaches you. It combines visual (red flash, text, 3D mesh), audio (sound effect), and timing (1-second delay) to create a complete jumpscare moment. The setTimeout function demonstrates asynchronous programming—scheduling an action to happen after a delay.

function doJumpScare() {
  console.log("JUMPSCARE!");
  // Flash red background
  background(RED[0], RED[1], RED[2]);
  fill(WHITE);
  textSize(80);
  textAlign(CENTER, CENTER);
  text("JUMPSCARE!", width / 2, height / 2);

  if (jumpscareSound) {
    jumpscareSound.play(); // Play the jumpscare sound
  }

  // Show the 3D jumpscare mesh briefly
  jumpscareFreddyMesh.visible = true;

  // After 1 second, hide the mesh and transition to gameover
  setTimeout(() => {
    jumpscareFreddyMesh.visible = false;
    gameState = 'gameover';
  }, 1000); // Display for 1 second
}
Line-by-line explanation (11 lines)

🔧 Subcomponents:

calculation Jumpscare visual effect background(RED[0], RED[1], RED[2]);

Flashes the screen red and displays large 'JUMPSCARE!' text for immediate visual impact

function-call Jumpscare sound jumpscareSound.play();

Plays an audio jump-scare sound effect if available

calculation 3D jumpscare mesh visibility jumpscareFreddyMesh.visible = true;

Makes the close-up 3D jumpscare model of Freddy visible on screen

function-call Delayed game-over transition setTimeout(() => {

Uses a 1-second timeout before transitioning to the 'gameover' game state, allowing the jumpscare to linger briefly

console.log("JUMPSCARE!");
Logs 'JUMPSCARE!' to the browser console (for debugging), letting you see in the console that the jumpscare was triggered.
background(RED[0], RED[1], RED[2]);
Fills the entire p5.js canvas with bright red (the RED color constant is [255, 0, 0]). This creates an immediate visual shock.
fill(WHITE);
Sets the text color to white so the next text will show up clearly against the red background.
textSize(80);
Makes the text huge (80 pixels) for maximum impact and visibility.
text("JUMPSCARE!", width / 2, height / 2);
Draws 'JUMPSCARE!' at the center of the screen. width/2 and height/2 center it horizontally and vertically.
if (jumpscareSound) {
Checks if the jumpscareSound was loaded before trying to play it. This prevents errors if the sound file is missing.
jumpscareSound.play();
Plays the pre-loaded audio file, adding an auditory scare to accompany the visual jumpscare.
jumpscareFreddyMesh.visible = true;
Makes the 3D jumpscare model of Freddy visible. This is a close-up, menacing version of his model with red eyes and wide mouth.
setTimeout(() => {
setTimeout is a JavaScript function that schedules code to run after a delay (in this case, 1000 milliseconds = 1 second).
jumpscareFreddyMesh.visible = false;
After 1 second, hide the 3D jumpscare mesh so it doesn't persist on the game-over screen.
gameState = 'gameover';
Changes the game state to 'gameover', which will trigger the game-over screen to display on the next draw() call.

keyPressed()

keyPressed() is a p5.js event handler that runs once every time a key is pressed. By checking the key or keyCode, you can implement keyboard controls. This game uses spacebar to toggle cameras, C/RIGHT ARROW to cycle cameras, and R to restart.

function keyPressed() {
  if (key === ' ' || keyCode === 32) { // Spacebar key
    if (gameState === 'playing') {
      camActive = !camActive; // Toggle camera active state only during playing
      if (!camActive && ambientHumSound && ambientHumSound.isPlaying()) {
        ambientHumSound.loop(); // Restart ambient hum when back in office
      } else if (camActive && ambientHumSound && ambientHumSound.isPlaying()) {
        ambientHumSound.stop(); // Stop ambient hum when on camera
      }
    }
  }
  // Cycle cameras when camActive is true
  if (gameState === 'playing' && camActive && (key === 'c' || key === 'C' || keyCode === RIGHT_ARROW)) {
    currentCamIndex = (currentCamIndex + 1) % cameraLocations.length;
    console.log(`Switched to: ${cameraLocations[currentCamIndex].name}`);
  }
  if (key === 'r' || key === 'R') { // 'R' key for restart
    if (gameState === 'gameover' || gameState === 'win') {
      restartGame();
    }
  }
}
Line-by-line explanation (12 lines)

🔧 Subcomponents:

conditional Spacebar camera toggle if (key === ' ' || keyCode === 32) {

Checks if the spacebar is pressed and toggles the camera on/off during gameplay

conditional Ambient sound management if (!camActive && ambientHumSound && ambientHumSound.isPlaying()) {

Plays or stops ambient sound based on whether the player is in office view or camera view

conditional Camera cycling if (gameState === 'playing' && camActive && (key === 'c' || key === 'C' || keyCode === RIGHT_ARROW)) {

Checks for C key or RIGHT ARROW to cycle through security cameras when in camera mode

conditional Restart game if (key === 'r' || key === 'R') {

Restarts the game when R is pressed during game-over or win screens

if (key === ' ' || keyCode === 32) {
Checks if the pressed key is a spacebar. The || means 'or'—it compares both key and keyCode to ensure reliability across different browsers.
if (gameState === 'playing') {
Only allow camera toggling during active gameplay. Don't allow it on the title, game-over, or win screens.
camActive = !camActive;
The ! operator inverts the camActive boolean. If it was true (on), it becomes false (off). If it was false (off), it becomes true (on).
if (!camActive && ambientHumSound && ambientHumSound.isPlaying()) {
Three conditions: if camActive is now false (you're back in the office), AND the ambient sound exists, AND it's currently playing, then restart it.
ambientHumSound.loop();
Plays the ambient hum sound in a loop. This gives the office a continuous background hum.
} else if (camActive && ambientHumSound && ambientHumSound.isPlaying()) {
Alternatively, if camActive is now true (you're on the camera), AND the ambient sound exists and is playing, then stop it.
ambientHumSound.stop();
Stops the ambient sound. On the camera feeds, you shouldn't hear the office ambiance.
if (gameState === 'playing' && camActive && (key === 'c' || key === 'C' || keyCode === RIGHT_ARROW)) {
Only allow camera cycling if: the game is playing, you're in camera mode, AND you pressed C (uppercase or lowercase) or RIGHT ARROW.
currentCamIndex = (currentCamIndex + 1) % cameraLocations.length;
Increments the camera index by 1. The % operator (modulo) wraps around: if you're at the last camera, the next press cycles back to the first camera.
if (key === 'r' || key === 'R') {
Checks if R is pressed (uppercase or lowercase).
if (gameState === 'gameover' || gameState === 'win') {
Only restart if the game is over or you've won. Don't allow restarting during active gameplay.
restartGame();
Calls the restartGame() function to reset all variables and begin a new game.

mousePressed()

mousePressed() is a p5.js event handler that runs once every time the mouse is clicked. By checking mouseX and mouseY against button rectangles, you can implement clickable UI elements. This game uses it to start gameplay from the title screen and to toggle doors and lights during gameplay.

function mousePressed() {
  if (gameState === 'title') {
    gameState = 'playing';
    restartGame(); // Use restartGame to initialize all variables
    return; // Prevent triggering door/light buttons immediately
  }

  if (gameState === 'playing' && !camActive) {
    // Check Left Door button (p5.js UI)
    if (mouseX > 0 && mouseX < 70 && mouseY > 150 && mouseY < 220) {
      leftDoorOpen = !leftDoorOpen;
      if (doorSound) doorSound.play();
    }
    // Check Right Door button (p5.js UI)
    if (mouseX > width - 70 && mouseX < width && mouseY > 150 && mouseY < 220) {
      rightDoorOpen = !rightDoorOpen;
      if (doorSound) doorSound.play();
    }
    // Check Left Light button (p5.js UI)
    if (mouseX > 0 && mouseX < 70 && mouseY > 370 && mouseY < 440) {
      leftLightOn = !leftLightOn;
      if (lightSound) lightSound.play();
    }
    // Check Right Light button (p5.js UI)
    if (mouseX > width - 70 && mouseX < width && mouseY > 370 && mouseY < 440) {
      rightLightOn = !rightLightOn;
      if (lightSound) lightSound.play();
    }
  }
}
Line-by-line explanation (10 lines)

🔧 Subcomponents:

conditional Title screen click handler if (gameState === 'title') {

Starts the game when the player clicks on the title screen

conditional Door button collision detection if (mouseX > 0 && mouseX < 70 && mouseY > 150 && mouseY < 220) {

Checks if the mouse click falls within the left door button's rectangular area and toggles the door

conditional Light button collision detection if (mouseX > 0 && mouseX < 70 && mouseY > 370 && mouseY < 440) {

Checks if the mouse click falls within the left light button's rectangular area and toggles the light

if (gameState === 'title') {
Check if the player is on the title screen.
gameState = 'playing';
Change the game state to 'playing', which transitions from the title screen to active gameplay.
restartGame();
Calls restartGame() to initialize all game variables (power, time, animatronic positions, etc.).
return;
Exits the function immediately. This prevents the button clicks below from being processed when the title is clicked.
if (gameState === 'playing' && !camActive) {
Only process button clicks if the game is playing AND the player is NOT in camera mode (i.e., in the office view).
if (mouseX > 0 && mouseX < 70 && mouseY > 150 && mouseY < 220) {
Checks if the mouse click is within the left door button's rectangle. && means ALL four conditions must be true (x between 0-70, y between 150-220).
leftDoorOpen = !leftDoorOpen;
Inverts the leftDoorOpen boolean. If the door was open, it closes; if it was closed, it opens.
if (doorSound) doorSound.play();
If the door sound was loaded, play it to give audio feedback when the door opens or closes.
if (mouseX > width - 70 && mouseX < width && mouseY > 150 && mouseY < 220) {
Checks if the click is in the right door button area. width - 70 positions it on the right edge of the screen.
if (mouseX > 0 && mouseX < 70 && mouseY > 370 && mouseY < 440) {
Checks if the click is in the left light button area (lower on the screen than the doors).

restartGame()

restartGame() resets every variable to starting values, creating a fresh game. This function is called when the player clicks the title screen to start or presses R on the game-over/win screens to restart. Good game design always includes a clear restart function—it's essential for replayability.

function restartGame() {
  power = 100.0;
  timeRemaining = 360;
  camActive = false;
  gameState = 'playing';
  currentCamIndex = 0; // Reset to first camera

  // Reset doors and lights
  leftDoorOpen = true;
  rightDoorOpen = true;
  leftLightOn = false;
  rightLightOn = false;

  // Reset animatronics
  freddyLocation = 'stage';
  freddyMoveTime = random(40, 80); // Randomize initial move time
  freddyMoveTimer = 0;
  freddyAtDoorTimer = 0;

  bonnieLocation = 'stage';
  bonnieMoveTime = random(30, 70);
  bonnieMoveTimer = 0;
  bonnieAtDoorTimer = 0;

  // Start ambient sound
  if (ambientHumSound && !ambientHumSound.isPlaying()) {
    ambientHumSound.loop();
  }
}
Line-by-line explanation (11 lines)

🔧 Subcomponents:

calculation Game variable reset power = 100.0;

Resets power, time, and camera state to starting values

calculation Office state reset leftDoorOpen = true;

Resets doors to open and lights to off, giving the player a fresh start

calculation Animatronic state reset freddyLocation = 'stage';

Returns both animatronics to the stage and randomizes their initial move times for variety

function-call Ambient sound restart ambientHumSound.loop();

Starts the ambient office hum sound for atmosphere

power = 100.0;
Sets power back to 100%, fully charged.
timeRemaining = 360;
Resets time to 360 seconds (6 in-game minutes = 1 minute per hour). The player must survive this long to win.
camActive = false;
Ensures the player starts in office view, not camera view.
gameState = 'playing';
Sets the game state to 'playing' so the main game loop begins.
currentCamIndex = 0;
Resets the camera index to 0, so pressing C will start on CAM 01.
leftDoorOpen = true;
Both doors start OPEN, so the player has no initial defense. They must close the doors to prevent entry.
leftLightOn = false;
Both lights start OFF to save power.
freddyLocation = 'stage';
Both Freddy and Bonnie start on the stage, visible to the player but not yet approaching.
freddyMoveTime = random(40, 80);
Randomizes how long Freddy waits before his first move (40-80 seconds). This variation keeps each game unique.
freddyMoveTimer = 0;
Resets Freddy's move timer to 0. When it counts up to freddyMoveTime, he will move.
ambientHumSound.loop();
Starts the ambient office hum sound, playing on repeat to set the atmosphere.

📦 Key Variables

power number

Stores the current power level (0-100%). Decreases every frame based on active systems (camera, doors, lights). Game ends if it reaches 0.

let power = 100.0;
timeRemaining number

Stores the time remaining in seconds (starting at 360 = 6 in-game minutes). Player wins if this reaches 0 without power running out.

let timeRemaining = 360;
gameState string

Tracks the current game screen: 'title', 'playing', 'gameover', 'win', or 'power_out'. Controls which draw function runs each frame.

let gameState = 'title';
camActive boolean

True when player is viewing security cameras, false when viewing the office. Toggled by spacebar.

let camActive = false;
leftDoorOpen boolean

True if the left door is open (animatronics can enter), false if closed (blocks them but drains power).

let leftDoorOpen = true;
rightDoorOpen boolean

True if the right door is open, false if closed. Works identically to leftDoorOpen for the right side.

let rightDoorOpen = true;
leftLightOn boolean

True if the left light is on (reveals animatronics at the left door), false if off. Drains power when on.

let leftLightOn = false;
rightLightOn boolean

True if the right light is on, false if off. Works identically to leftLightOn for the right side.

let rightLightOn = false;
freddyLocation string

Stores Freddy's current position: 'stage', 'hall', 'office_left_door', or 'office'. Controls his visibility and behavior.

let freddyLocation = 'stage';
freddyMoveTime number

Time in seconds before Freddy moves to the next location. Randomized each move for unpredictability.

let freddyMoveTime = 60;
freddyMoveTimer number

Counts up each frame. When it reaches freddyMoveTime, Freddy moves and the timer resets.

let freddyMoveTimer = 0;
freddyAtDoorTimer number

Counts time Freddy waits at the left door. When it reaches freddyAtDoorDelay and the door is open, he enters.

let freddyAtDoorTimer = 0;
bonnieLocation string

Stores Bonnie's current position: 'stage', 'hall', 'office_right_door', or 'office'. Controls her visibility and behavior.

let bonnieLocation = 'stage';
bonnieMoveTime number

Time in seconds before Bonnie moves. Generally faster than Freddy, making her more aggressive.

let bonnieMoveTime = 45;
bonnieMoveTimer number

Counts up each frame. When it reaches bonnieMoveTime, Bonnie moves and the timer resets.

let bonnieMoveTimer = 0;
scene object

The three.js Scene object containing all 3D geometry, lights, and cameras. All 3D objects are added to this.

let scene;
renderer object

The three.js WebGLRenderer that draws the 3D scene. Called each frame by draw3D().

let renderer;
freddyMesh object

A three.js Group containing all parts of Freddy's 3D model (head, body, limbs, etc.). Positioned and rotated each frame.

let freddyMesh;
bonnieMesh object

A three.js Group containing all parts of Bonnie's 3D model. Works identically to freddyMesh.

let bonnieMesh;
currentCamIndex number

Index into the cameraLocations array. Incremented by C key or RIGHT ARROW to cycle between cameras.

let currentCamIndex = 0;
cameraLocations array

Array of objects, each containing a camera name and a three.js camera object. Allows cycling between security camera views.

let cameraLocations = [];

🔧 Potential Improvements (6)

Here are some ways this code could be enhanced:

BUG doJumpScare()

The setTimeout callback can be called multiple times if Freddy or Bonnie trigger jumpscare repeatedly in the same frame before gameState changes to 'gameover'.

💡 Add a flag like 'jumpscareTriggered' set to true in doJumpScare(), and check it in checkJumpScare() to prevent duplicate jumpscare calls: if (jumpscareTriggered) return;

PERFORMANCE update3DScene()

The function sets visibility and position every frame for both animatronics even when they're not visible, which is unnecessary work.

💡 Add early returns: if freddyMesh.visible is already false and freddy isn't near an office door, skip the position update.

STYLE Variable declarations at top of file

There are many global variables (power, timeRemaining, freddyLocation, etc.) scattered throughout the top section, making the code harder to scan.

💡 Group related variables together with comments: // === GAME STATE ===, // === ANIMATRONIC STATES ===, etc. to organize the globals.

FEATURE mousePressed()

Button click zones are hard-coded as magic numbers (0, 70, 150, 220, etc.), making it difficult to reposition or resize buttons.

💡 Create button objects with properties like {x, y, w, h, label} and loop through them to check clicks, then use the same objects in drawDoorsButtons() for consistent positioning.

BUG checkFreddyMove() and checkBonnieMove()

The deltaTime variable is used inside the door timer increment, but the function runs inside drawGameUI() which already increments the timer. This can cause double-counting if not careful.

💡 Move all deltaTime-based updates to a single update function, not spread across multiple functions. Clarify which variables use deltaTime and which use manual timer increments.

STYLE createFreddyMesh() and createBonnieMesh()

Both functions are extremely long (300+ lines each) with repetitive code for body parts (head, arms, legs, etc.).

💡 Create a helper function like createLimb(geometry, material, position) to reduce repetition, then call it multiple times with different parameters.

🔄 Code Flow

Code flow showing setup, setup3d, draw, update3dscene, drawgameui, checkfreddymove, checkjumpscare, dojumpscare, keypressed, mousepressed, restartgame

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

graph TD start[Start] --> setup[setup] setup --> canvas-setup[Canvas Setup] setup --> 3d-init[3D Initialization] canvas-setup --> setup3d[setup3D] setup3d --> scene-creation[Scene Creation] setup3d --> renderer-setup[Renderer Setup] setup3d --> camera-array[Camera Array] setup3d --> light-setup[Light Setup] setup3d --> geometry-creation[Geometry Creation] setup --> draw[draw loop] draw --> update3dscene[update3DScene] update3dscene --> 3d-update[Update 3D Scene State] update3dscene --> door-visibility[Door Visibility Toggle] update3dscene --> light-visibility[Light Visibility Toggle] update3dscene --> camera-select[Camera Selection] update3dscene --> freddy-position[Freddy Position Update] update3dscene --> bonnie-position[Bonnie Position Update] draw --> drawgameui[drawGameUI] drawgameui --> drain-calc[Power Drain Calculation] drawgameui --> power-update[Power and Timer Updates] drawgameui --> ai-update[Animatronic Movement Logic] drawgameui --> gamestate-check[Win/Lose Condition Checks] drawgameui --> ui-draw[UI Drawing Dispatcher] drawgameui --> state-switch[Game State Dispatcher] state-switch --> title-click[Title Click Handler] state-switch --> early-return[Early Function Return] state-switch --> spacebar-toggle[Spacebar Camera Toggle] state-switch --> audio-toggle[Ambient Sound Management] state-switch --> camera-cycle[Camera Cycling] state-switch --> restart-key[Restart Game] draw --> canvas-clear[Canvas Clear] canvas-clear --> 3d-render[Render 3D Graphics] click setup href "#fn-setup" click setup3d href "#fn-setup3d" click draw href "#fn-draw" click update3dscene href "#fn-update3dscene" click drawgameui href "#fn-drawgameui" click drain-calc href "#sub-drain-calc" click power-update href "#sub-power-update" click ai-update href "#sub-ai-update" click gamestate-check href "#sub-gamestate-check" click ui-draw href "#sub-ui-draw" click state-switch href "#sub-state-switch" click title-click href "#sub-title-click" click early-return href "#sub-early-return" click spacebar-toggle href "#sub-spacebar-toggle" click audio-toggle href "#sub-audio-toggle" click camera-cycle href "#sub-camera-cycle" click restart-key href "#sub-restart-key" click canvas-clear href "#sub-canvas-clear" click 3d-update href "#sub-3d-update" click door-visibility href "#sub-door-visibility" click light-visibility href "#sub-light-visibility" click camera-select href "#sub-camera-select" click freddy-position href "#sub-freddy-position" click bonnie-position href "#sub-bonnie-position" click 3d-render href "#sub-3d-render"

❓ Frequently Asked Questions

What kind of visuals can users expect from the 'five night at diddys' sketch?

The sketch creates a dark, atmospheric environment reminiscent of a horror game, featuring an office setting with animated animatronics that move closer to the player as time progresses.

How can players interact with the 'five night at diddys' game?

Players can manage their power by controlling lights and doors to defend against animatronics, while monitoring their movement through a series of game hours.

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

This sketch demonstrates game state management, timer-based mechanics for character movement, and the integration of audio for immersive gameplay.

Preview

five night at diddys - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of five night at diddys - Code flow showing setup, setup3d, draw, update3dscene, drawgameui, checkfreddymove, checkjumpscare, dojumpscare, keypressed, mousepressed, restartgame
Code Flow Diagram