IT SEES.

This sketch creates an immersive first-person 3D maze where the player navigates using WASD and mouse look while being hunted by a terrifying, twitching creature with glowing red eyes. A flickering flashlight illuminates the foggy maze as walls fade to black with distance, and a survival timer tracks how long you evade the relentless stalker before being caught.

🧪 Try This!

Experiment with the code by making these changes:

  1. Change the maze layout — Edit the maze array to create a different path structure—change 0s to 1s to add walls, or 1s to 0s to open passages
  2. Make the stalker glow blue instead of red — Change the eye color from red (255, 0, 0) to blue (0, 0, 255)—the stalker will emit glowing blue light instead
  3. Make movement controls inverted — Swap the direction vectors so pressing W moves backward—this makes the controls feel 'opposite'
  4. Start the game in a different location — Move the player spawn point to a different corner of the maze by changing the initial position
  5. Make the flashlight wider — The spotLight's cone angle (PI/3.5) controls width—change it to PI/2 for a much wider beam
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch builds a playable horror game inside a 3D maze rendered with p5.js's WEBGL mode. The player controls a first-person camera using mouse look and WASD keys, navigating through towering walls while a creature with glowing red eyes hunts them relentlessly. Visual atmosphere is created through distance-based fog that fades walls to black, a flickering spotlight flashlight, minimal ambient light, and a twitching, spindly stalker that accelerates as survival time increases.

The code is organized into core gameplay loops: collision-aware movement, AI stalker pathfinding, camera/lighting setup, and 3D rendering of the maze and creature. By studying it you will learn how to build first-person games in p5.js using WEBGL's camera() and lighting functions, implement sliding collision detection on a grid-based maze, create simple but effective AI that chases the player, use distance-based fog for atmosphere, and orchestrate multiple 3D elements into a cohesive interactive experience.

⚙️ How It Works

  1. When the sketch loads, setup() creates a full-window WEBGL canvas, initializes the player at position (600, 600) inside the maze, and places the stalker at (3400, 3000). It also enables pointer lock so mouse movements control camera rotation.
  2. Every frame, draw() handles player input: WASD keys move forward/backward/left/right relative to the camera's rotation, and mouse movement controlled by movedX rotates the view. The player's next position is checked against the maze grid, and only collision-free moves are applied (sliding collision).
  3. The stalker AI calculates the distance and direction to the player, then moves toward them at an increasing speed that accelerates every second. If the stalker gets within 110 pixels, gameOver() is triggered.
  4. A camera positioned at the player's eyes looks in the direction of rotation, with a breathing bob applied via sin() for realism. A spotLight flashlight follows the camera direction but flickers randomly, and ambientLight() is kept near-black for horror atmosphere.
  5. The drawWorld() function renders every wall in the maze, applying distance-based fog by using lerpColor() to fade wall colors toward black the farther they are from the player. A floor plane is also drawn and faded.
  6. The drawNightmare() function renders the stalker at its current position: a twitching black body, two glowing red spheres for eyes, and ten animated spindly lines for limbs that twitch with frameCount-based sine waves, rotated to face the player.

🎓 Concepts You'll Learn

WEBGL 3D renderingFirst-person camera controlCollision detection on gridFog and distance-based effectsAI pathfinding and pursuitLighting and spotlightsPointer lock for mouse lookGame state and game overTrigonometry for direction

📝 Code Breakdown

setup()

setup() runs once at the start. In a game, use it to initialize canvas, set player and enemy positions, and enable input methods like pointer lock.

function setup() {
  createCanvas(windowWidth, windowHeight, WEBGL);
  frameRate(60);
  px = cellSize * 1.5; pz = cellSize * 1.5;
  sx = cellSize * 8.5; sz = cellSize * 7.5;
  canvas.onclick = () => { if(!document.pointerLockElement) requestPointerLock(); };
}
Line-by-line explanation (5 lines)
createCanvas(windowWidth, windowHeight, WEBGL);
Creates a full-window 3D canvas using WEBGL mode, which enables 3D rendering and lighting
frameRate(60);
Locks the animation to 60 frames per second for smooth gameplay
px = cellSize * 1.5; pz = cellSize * 1.5;
Initializes the player's starting position at (600, 600) inside the maze—roughly cell (1,1)
sx = cellSize * 8.5; sz = cellSize * 7.5;
Initializes the stalker's starting position at (3400, 3000)—far away in the maze, in the upper right
canvas.onclick = () => { if(!document.pointerLockElement) requestPointerLock(); };
When the canvas is clicked, requests pointer lock so mouse movements control camera rotation instead of moving the cursor

draw()

draw() is the game loop that runs 60 times per second. Every frame it handles input, updates positions, checks collisions and win/lose conditions, and renders everything. This is where all gameplay happens.

🔬 This is the AI chase logic: it calculates distance and moves toward you. What happens if you change (dx / d) to (dx / d) * 0.5 so the stalker only moves half-speed toward you?

  let dx = px - sx, dz = pz - sz;
  let d = sqrt(dx*dx + dz*dz);
  let currentStalkerSpeed = stalkerSpeed + (survivalTime * 0.05);
  sx += (dx / d) * currentStalkerSpeed;
  sz += (dz / d) * currentStalkerSpeed;
function draw() {
  background(0);
  if (frameCount % 60 === 0) survivalTime++;

  if (document.pointerLockElement === canvas) rx += movedX * 0.0025;

  let currentSpeed = keyIsDown(SHIFT) ? baseSpeed * 1.8 : baseSpeed;
  let vx = 0, vz = 0;
  if (keyIsDown(87)) { vx += sin(rx); vz -= cos(rx); }
  if (keyIsDown(83)) { vx -= sin(rx); vz += cos(rx); }
  if (keyIsDown(65)) { vx -= cos(rx); vz -= sin(rx); }
  if (keyIsDown(68)) { vx += cos(rx); vz += sin(rx); }

  let buffer = 50;
  let nextX = px + vx * currentSpeed;
  if (maze[floor(pz/cellSize)][floor((nextX + (vx > 0 ? buffer : -buffer))/cellSize)] === 0) px = nextX;
  let nextZ = pz + vz * currentSpeed;
  if (maze[floor((nextZ + (vz > 0 ? buffer : -buffer))/cellSize)][floor(px/cellSize)] === 0) pz = nextZ;

  let dx = px - sx, dz = pz - sz;
  let d = sqrt(dx*dx + dz*dz);
  let currentStalkerSpeed = stalkerSpeed + (survivalTime * 0.05);
  sx += (dx / d) * currentStalkerSpeed;
  sz += (dz / d) * currentStalkerSpeed;

  if (d < 110) { gameOver(); return; }

  let breathe = sin(frameCount * 0.07) * 6;
  camera(px, breathe, pz, px + sin(rx), breathe, pz - cos(rx), 0, 1, 0);
  
  let flicker = noise(frameCount * 0.1) > 0.06 ? 255 : random(20, 120);
  spotLight(flicker, flicker, 180, px, 0, pz, sin(rx), 0, -cos(rx), PI/3.5, 2);
  ambientLight(5);

  drawWorld();
}
Line-by-line explanation (25 lines)

🔧 Subcomponents:

conditional Mouse Look Rotation if (document.pointerLockElement === canvas) rx += movedX * 0.0025;

Converts mouse movement into camera rotation when pointer is locked

conditional Sprint Speed Boost let currentSpeed = keyIsDown(SHIFT) ? baseSpeed * 1.8 : baseSpeed;

Doubles movement speed when Shift is held down

for-loop WASD Direction Calculation if (keyIsDown(87)) { vx += sin(rx); vz -= cos(rx); }

Converts WASD key presses into velocity components that rotate with the camera

conditional Sliding Collision Detection if (maze[floor(pz/cellSize)][floor((nextX + (vx > 0 ? buffer : -buffer))/cellSize)] === 0) px = nextX;

Only moves the player if the target cell in the maze is passable (0, not 1)

calculation Stalker AI Pursuit sx += (dx / d) * currentStalkerSpeed;

Moves stalker toward player by normalizing distance vector and multiplying by speed

conditional Catch Detection if (d < 110) { gameOver(); return; }

Triggers game over when stalker gets too close

background(0);
Fills the canvas with pure black each frame, erasing the previous frame's visuals
if (frameCount % 60 === 0) survivalTime++;
Increments the survival counter once per second (every 60 frames at 60 fps)
if (document.pointerLockElement === canvas) rx += movedX * 0.0025;
When pointer is locked, adds mouse horizontal movement to the rotation angle rx, creating mouse look
let currentSpeed = keyIsDown(SHIFT) ? baseSpeed * 1.8 : baseSpeed;
Sets movement speed to 1.8x normal when Shift is held, otherwise uses base speed
let vx = 0, vz = 0;
Initializes velocity components to zero before checking which keys are pressed
if (keyIsDown(87)) { vx += sin(rx); vz -= cos(rx); }
Key 87 is 'W'; adds forward velocity components that rotate with the camera angle rx
if (keyIsDown(83)) { vx -= sin(rx); vz += cos(rx); }
Key 83 is 'S'; adds backward velocity (opposite of W)
if (keyIsDown(65)) { vx -= cos(rx); vz -= sin(rx); }
Key 65 is 'A'; adds leftward velocity perpendicular to camera angle
if (keyIsDown(68)) { vx += cos(rx); vz += sin(rx); }
Key 68 is 'D'; adds rightward velocity
let buffer = 50;
Collision buffer: used to check the maze slightly ahead of the player to prevent walking into walls
let nextX = px + vx * currentSpeed;
Calculates where the player would be if they move in x direction
if (maze[floor(pz/cellSize)][floor((nextX + (vx > 0 ? buffer : -buffer))/cellSize)] === 0) px = nextX;
Converts world coordinates to maze grid indices, checks if the cell ahead is passable (0), and only updates px if safe
let nextZ = pz + vz * currentSpeed;
Calculates where the player would be if they move in z direction
if (maze[floor((nextZ + (vz > 0 ? buffer : -buffer))/cellSize)][floor(px/cellSize)] === 0) pz = nextZ;
Same collision check for z movement—this is 'sliding' collision, allowing diagonal movement around corners
let dx = px - sx, dz = pz - sz;
Calculates the vector from stalker to player (needed for distance and direction)
let d = sqrt(dx*dx + dz*dz);
Calculates distance between player and stalker using Pythagorean theorem
let currentStalkerSpeed = stalkerSpeed + (survivalTime * 0.05);
Stalker accelerates by 0.05 units per second survived, making it harder to escape over time
sx += (dx / d) * currentStalkerSpeed;
Moves stalker toward player: dividing dx by d normalizes the direction, then multiplies by speed
sz += (dz / d) * currentStalkerSpeed;
Same as above but for z axis
if (d < 110) { gameOver(); return; }
If stalker is within 110 pixels, game is lost—trigger game over and stop drawing
let breathe = sin(frameCount * 0.07) * 6;
Creates a bobbing animation using sine wave—camera height oscillates ±6 pixels to simulate breathing
camera(px, breathe, pz, px + sin(rx), breathe, pz - cos(rx), 0, 1, 0);
Positions the first-person camera at player's location looking in the direction of rotation rx, with y-up orientation
let flicker = noise(frameCount * 0.1) > 0.06 ? 255 : random(20, 120);
Creates flickering effect: 94% of frames use Perlin noise for natural flicker, 6% jump to random brightness for scary glitches
spotLight(flicker, flicker, 180, px, 0, pz, sin(rx), 0, -cos(rx), PI/3.5, 2);
Creates a flickering spotlight at player's location pointing in camera direction—angle PI/3.5 is the cone width
ambientLight(5);
Sets nearly-black ambient light (5/255 brightness) so walls are only visible by flashlight

drawWorld()

drawWorld() handles all rendering: iterating through the maze grid, calculating fog for each wall, and drawing the stalker. This is where the visual atmosphere comes from—the fog calculation is the key to the horror feeling.

function drawWorld() {
  noStroke();
  let wallColor = color(40, 45, 50);
  let black = color(0);

  for (let r = 0; r < maze.length; r++) {
    for (let c = 0; c < maze[r].length; c++) {
      if (maze[r][c] === 1) {
        let wx = c * cellSize + cellSize/2;
        let wz = r * cellSize + cellSize/2;
        let distToPlayer = dist(px, pz, wx, wz);
        
        let fogAmount = map(distToPlayer, 0, cellSize * 4, 0, 1);
        let finalColor = lerpColor(wallColor, black, constrain(fogAmount, 0, 1));
        
        push();
        translate(wx, 0, wz);
        ambientMaterial(finalColor);
        box(cellSize, 1000, cellSize);
        pop();
      }
    }
  }
  
  push();
  translate(cellSize * 5, 500, cellSize * 4.5);
  rotateX(HALF_PI);
  ambientMaterial(15);
  plane(cellSize * 15, cellSize * 15);
  pop();
  
  drawNightmare();
}
Line-by-line explanation (23 lines)

🔧 Subcomponents:

for-loop Maze Grid Iteration for (let r = 0; r < maze.length; r++) {

Loops through every row in the maze array

for-loop Wall Rendering if (maze[r][c] === 1) {

Checks if current cell is a wall (1) and renders it if true

calculation Distance-Based Fog let fogAmount = map(distToPlayer, 0, cellSize * 4, 0, 1);

Maps distance to a 0-1 fog intensity that fades walls to black

calculation Color Interpolation let finalColor = lerpColor(wallColor, black, constrain(fogAmount, 0, 1));

Blends between gray wall color and black based on distance

noStroke();
Turns off outlines for all shapes so walls render smoothly without edges
let wallColor = color(40, 45, 50);
Defines a dark gray color (RGB 40,45,50) used for walls at close distance
let black = color(0);
Defines pure black, the fog color that walls fade toward at distance
for (let r = 0; r < maze.length; r++) {
Outer loop: iterates through each row of the maze (9 rows)
for (let c = 0; c < maze[r].length; c++) {
Inner loop: iterates through each column in the row (10 columns)
if (maze[r][c] === 1) {
Checks if the current cell is a wall (1 means wall, 0 means passable space)
let wx = c * cellSize + cellSize/2;
Converts column index to world x coordinate—adds half cellSize to center the wall in its cell
let wz = r * cellSize + cellSize/2;
Converts row index to world z coordinate—adds half cellSize to center the wall in its cell
let distToPlayer = dist(px, pz, wx, wz);
Calculates Euclidean distance from player to this wall's center
let fogAmount = map(distToPlayer, 0, cellSize * 4, 0, 1);
Maps distance to a 0-1 value: at distance 0 fog is 0 (no fading), at distance cellSize*4 fog is 1 (full black)
let finalColor = lerpColor(wallColor, black, constrain(fogAmount, 0, 1));
Interpolates between gray and black based on fog amount, clamped to 0-1 range
push();
Saves the current transformation matrix before translating and rotating
translate(wx, 0, wz);
Moves the origin to the wall's position in the world
ambientMaterial(finalColor);
Sets the wall's material to reflect only ambient light in the calculated fog color
box(cellSize, 1000, cellSize);
Draws a tall box (1000 pixels high) centered at the origin—creates the wall effect
pop();
Restores the transformation matrix to what it was before this wall
push();
Saves the transformation matrix before drawing the floor
translate(cellSize * 5, 500, cellSize * 4.5);
Moves the floor to center it roughly in the middle of the maze world
rotateX(HALF_PI);
Rotates 90 degrees around the x-axis to lay the plane flat on the ground
ambientMaterial(15);
Sets floor material to very dark gray, making it barely visible in the darkness
plane(cellSize * 15, cellSize * 15);
Draws a 6000x6000 pixel plane to serve as the ground
pop();
Restores the transformation matrix
drawNightmare();
Calls the function that renders the stalker creature

drawNightmare()

drawNightmare() renders the creature pursuing the player. Every part—body, eyes, limbs—uses frameCount-based sine waves to create continuous unsettling movement. The emissiveMaterial for eyes is key: it makes them glow without needing external light.

🔬 These lines draw and animate each limb. What happens if you change the 180 to 100 and the 80 to 40, so limbs are shorter? Or change 0.3 to 0.8 to make twitching faster?

    rotateZ(PI / 5 * i + sin(frameCount * 0.3));
    let len = 180 + sin(frameCount * 0.4 + i) * 80;
    line(0, 0, 0, len, sin(frameCount * 0.8) * 150, cos(i) * 70);
function drawNightmare() {
  push();
  translate(sx, 0, sz);
  rotateY(atan2(px - sx, pz - sz));
  
  fill(0);
  push();
  translate(random(-4, 4), sin(frameCount * 0.2) * 15, random(-4, 4));
  box(60, 400, 60);
  pop();

  emissiveMaterial(255, 0, 0);
  push();
  translate(-18, -140, 35);
  sphere(12);
  translate(36, 0, 0);
  sphere(12);
  pop();

  stroke(0);
  strokeWeight(5);
  for (let i = 0; i < 10; i++) {
    push();
    rotateZ(PI / 5 * i + sin(frameCount * 0.3));
    let len = 180 + sin(frameCount * 0.4 + i) * 80;
    line(0, 0, 0, len, sin(frameCount * 0.8) * 150, cos(i) * 70);
    pop();
  }
  pop();
}
Line-by-line explanation (24 lines)

🔧 Subcomponents:

calculation Body Twitching translate(random(-4, 4), sin(frameCount * 0.2) * 15, random(-4, 4));

Adds random jitter and vertical bobbing to the body for unnerving movement

calculation Glowing Red Eyes emissiveMaterial(255, 0, 0);

Makes eyes glow red and emit light rather than just reflect it

for-loop Animated Limbs for (let i = 0; i < 10; i++) {

Draws 10 twitching spindly limbs radiating from the body

push();
Saves the transformation matrix before positioning the stalker
translate(sx, 0, sz);
Moves the origin to the stalker's current position in world space
rotateY(atan2(px - sx, pz - sz));
Rotates the stalker to face the player: atan2() calculates the angle from stalker to player
fill(0);
Sets the fill color to black for the body
push();
Saves the transformation matrix again before twitching the body
translate(random(-4, 4), sin(frameCount * 0.2) * 15, random(-4, 4));
Adds random jitter (±4 pixels in x and z) and smooth vertical bobbing (±15 pixels in y) to simulate twitching
box(60, 400, 60);
Draws the stalker's body as a 60x400x60 box (tall and narrow, like a torso)
pop();
Restores the transformation before the twitch
emissiveMaterial(255, 0, 0);
Sets material to emit red light, making the eyes glow rather than reflect
push();
Saves the transformation matrix before positioning the eyes
translate(-18, -140, 35);
Moves to the left eye position relative to the stalker's body center
sphere(12);
Draws a glowing red sphere (radius 12) for the left eye
translate(36, 0, 0);
Moves 36 pixels to the right (from left eye to right eye position)
sphere(12);
Draws the second glowing red sphere for the right eye
pop();
Restores the transformation
stroke(0);
Sets line color to black for the limbs
strokeWeight(5);
Sets line thickness to 5 pixels for visible limbs
for (let i = 0; i < 10; i++) {
Loops 10 times to draw 10 limbs radiating from the body
push();
Saves the transformation before rotating each limb
rotateZ(PI / 5 * i + sin(frameCount * 0.3));
Rotates each limb: spreads them evenly (PI/5 * i) and adds a twitching oscillation (sin term)
let len = 180 + sin(frameCount * 0.4 + i) * 80;
Calculates limb length: base 180 pixels plus a ±80 pixel oscillation that differs for each limb
line(0, 0, 0, len, sin(frameCount * 0.8) * 150, cos(i) * 70);
Draws a line from the origin to a point in space: length changes with 'len', and y/z coordinates twitch to create spindly movement
pop();
Restores the transformation before the next limb
pop();
Restores the original transformation matrix

gameOver()

gameOver() is the losing state. It freezes the animation, switches to 2D mode for text overlay, and displays the player's score. This pattern—halting the loop, switching camera/projection modes, and overlaying UI—is useful for pause screens and menu systems too.

function gameOver() {
  camera();
  ortho();
  background(10, 0, 0);
  
  fill(255, 0, 0);
  textAlign(CENTER, CENTER);
  
  textSize(60);
  text("RECLAIMED BY THE VOID", 0, -40);
  
  fill(200);
  textSize(24);
  text("SURVIVED: " + survivalTime + " SECONDS", 0, 40);
  text("CLICK TO RESTART", 0, 100);
  
  noLoop();
  
  canvas.onclick = () => { window.location.reload(); };
}
Line-by-line explanation (13 lines)

🔧 Subcomponents:

calculation Camera Reset camera();

Resets to default 2D orthographic camera for text overlay

calculation 2D Rendering Mode ortho();

Switches from 3D perspective to 2D orthographic projection

calculation Stop Loop noLoop();

Halts the draw() loop so animation stops

camera();
Resets the camera to default settings, removing the first-person perspective
ortho();
Switches to 2D orthographic projection (no perspective), making text and UI rendering much easier
background(10, 0, 0);
Fills the screen with a very dark reddish color (10,0,0) to signal the horror theme of losing
fill(255, 0, 0);
Sets text fill color to pure red for the main game over message
textAlign(CENTER, CENTER);
Centers all subsequent text both horizontally and vertically around the coordinates given
textSize(60);
Sets text size to 60 pixels for the main message
text("RECLAIMED BY THE VOID", 0, -40);
Draws the game over message in large red text at position (0, -40) relative to canvas center
fill(200);
Changes text color to gray for secondary information
textSize(24);
Reduces text size to 24 pixels for smaller messages
text("SURVIVED: " + survivalTime + " SECONDS", 0, 40);
Displays the player's survival time, concatenating the survivalTime variable into the string
text("CLICK TO RESTART", 0, 100);
Instructs the player how to restart the game
noLoop();
Stops the draw() function from running, freezing the game state on screen
canvas.onclick = () => { window.location.reload(); };
Replaces the pointer lock request with a click handler that reloads the page to restart

📦 Key Variables

px number

Player's x position in world space—updated by WASD movement and collision detection

let px = 600;
pz number

Player's z position in world space—updated by WASD movement and collision detection

let pz = 600;
rx number

Player's rotation angle in radians—updated by mouse movement (movedX) when pointer is locked, controls camera direction

let rx = 0;
cellSize number

Width/height of each maze cell in pixels—used to convert between grid indices and world coordinates

let cellSize = 400;
baseSpeed number

Player's normal movement speed in pixels per frame—doubled when Shift is held

let baseSpeed = 13.0;
sx number

Stalker's x position in world space—updated by AI pathfinding toward the player

let sx = 3400;
sz number

Stalker's z position in world space—updated by AI pathfinding toward the player

let sz = 3000;
stalkerSpeed number

Base speed of the stalker's movement in pixels per frame—accelerates over time as survival time increases

let stalkerSpeed = 4.2;
survivalTime number

Tracks how many seconds the player has survived—increments once per second and accelerates the stalker

let survivalTime = 0;
maze array

2D grid representing the maze layout: 1 = wall, 0 = passable space; 9x10 array

let maze = [[1,1,1,...],[1,0,0,...],...]; // 9 rows, 10 columns

🔧 Potential Improvements (5)

Here are some ways this code could be enhanced:

BUG draw() - stalker AI pathfinding

If player and stalker spawn at exactly the same position, dividing by distance (d = 0) causes division by zero, crashing the AI

💡 Add a safety check: if (d > 0) before updating stalker position, or initialize them far apart (which is already done)

PERFORMANCE drawWorld() - maze rendering

Every frame, all 90 cells are checked and many walls are rendered even if far away and invisible due to fog

💡 Only render walls within a certain distance of the player, or use frustum culling to skip off-camera walls entirely

STYLE draw() - movement input handling

WASD key codes (87, 83, 65, 68) are hardcoded magic numbers, making the code unclear and hard to maintain

💡 Define constants at the top: const W = 87, S = 83, A = 65, D = 68; then use keyIsDown(W) instead

FEATURE sketch overall

No score/high score saving—survival times are lost when the page reloads

💡 Use localStorage to save best survival time: localStorage.setItem('bestTime', survivalTime); and display it on game over

BUG gameOver() - restart handler

If gameOver() is called multiple times before page reload, multiple click handlers are registered, causing unexpected behavior

💡 Use a flag to ensure gameOver() only runs once, or remove the previous onclick handler before assigning the new one

🔄 Code Flow

Code flow showing setup, draw, drawworld, drawnightmare, gameover

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

graph TD start[Start] --> setup[setup] setup --> draw[draw loop] draw --> mouselook[Mouse Look Rotation] draw --> sprintlogic[Sprint Speed Boost] draw --> wasdmovement[WASD Direction Calculation] draw --> collisioncheck[Sliding Collision Detection] draw --> stalkerchase[Stalker AI Pursuit] draw --> catchcheck[Catch Detection] draw --> drawworld[drawworld] drawworld --> mazeloop[Maze Grid Iteration] mazeloop --> wallloop[Wall Rendering] wallloop --> fogcalculation[Distance-Based Fog] fogcalculation --> colorlerp[Color Interpolation] drawworld --> drawnightmare[drawnightmare] drawnightmare --> bodytwitch[Body Twitching] drawnightmare --> eyeRendering[Glowing Red Eyes] drawnightmare --> limbloop[Animated Limbs] draw --> gameover[gameover] gameover --> stopanimation[Stop Loop] gameover --> resetcamera[Camera Reset] gameover --> twodmode[2D Rendering Mode] click setup href "#fn-setup" click draw href "#fn-draw" click mouselook href "#sub-mouse-look" click sprintlogic href "#sub-sprint-logic" click wasdmovement href "#sub-wasd-movement" click collisioncheck href "#sub-collision-check" click stalkerchase href "#sub-stalker-chase" click catchcheck href "#sub-catch-check" click drawworld href "#fn-drawworld" click mazeloop href "#sub-maze-loop" click wallloop href "#sub-wall-loop" click fogcalculation href "#sub-fog-calculation" click colorlerp href "#sub-color-lerp" click drawnightmare href "#fn-drawnightmare" click bodytwitch href "#sub-body-twitch" click eyeRendering href "#sub-eye-rendering" click limbloop href "#sub-limb-loop" click gameover href "#fn-gameover" click stopanimation href "#sub-stop-animation" click resetcamera href "#sub-reset-camera" click twodmode href "#sub-2d-mode"

❓ Frequently Asked Questions

What visual experience does the IT SEES sketch provide?

The IT SEES sketch creates a haunting visual experience of navigating a dim, foggy 3D maze, where towering walls fade into darkness and a flickering flashlight reveals the surroundings.

How can users navigate and interact with the IT SEES sketch?

Users can navigate the maze using the WASD keys for movement and mouse look to change their view, while a relentless stalker hunts them down.

What creative coding concepts are showcased in the IT SEES sketch?

This sketch demonstrates techniques such as 3D maze generation, collision detection, and dynamic AI behavior for an immersive survival experience.

Preview

IT SEES. - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of IT SEES. - Code flow showing setup, draw, drawworld, drawnightmare, gameover
Code Flow Diagram