Node3D class
Node3D represents a single vertex on Eric's head mesh. Each of the 35×35 vertices uses the same physics engine (verlet integration with spring forces and damping). The meltFactor smoothly transitions stiffness and damping to make the physics feel less bouncy and more liquid-like. This is the core technique behind soft-body physics: every vertex wants to return to its target but is held back by damping, creating jelly-like deformation.
🔬 These two lines swap values as Eric melts. What happens if you swap them—so stiffness gets stronger when melting? Try changing the first 0.15 to 0.04 and the 0.04 to 0.15. Will Eric get bouncier or stiffer as a puddle?
let stiffness = lerp(0.15, 0.04, meltFactor); // Looser spring
let damping = lerp(0.82, 0.94, meltFactor); // More sloshy fluid friction
class Node3D {
constructor(x, y, z, u, v, lat, lon) {
this.rest = createVector(x, y, z);
// Puddle mode dynamically flattens and widens the face!
this.puddleRest = createVector(x * 2.5, 260 + y * 0.1, z * 2.5);
this.target = createVector(x, y, z);
this.pos = createVector(x, y, z);
this.vel = createVector(0, 0, 0);
this.u = u; this.v = v;
this.lat = lat; this.lon = lon;
}
update() {
// Dynamically change physics to fluid when melting
let stiffness = lerp(0.15, 0.04, meltFactor); // Looser spring
let damping = lerp(0.82, 0.94, meltFactor); // More sloshy fluid friction
let force = p5.Vector.sub(this.target, this.pos);
force.mult(stiffness);
this.vel.add(force);
this.vel.mult(damping);
this.pos.add(this.vel);
}
}
Line-by-line explanation (11 lines)
🔧 Subcomponents:
this.puddleRest = createVector(x * 2.5, 260 + y * 0.1, z * 2.5);
Pre-calculates where this vertex moves when melting (wider and flatter than the original sphere)
let force = p5.Vector.sub(this.target, this.pos);
Calculates the vector from current position to target, which becomes the spring force
this.vel.mult(damping);
Reduces velocity each frame, simulating air resistance and preventing infinite bouncing
this.rest = createVector(x, y, z);- Stores the original 3D position of this vertex on the sphere—the resting state before any deformation
this.puddleRest = createVector(x * 2.5, 260 + y * 0.1, z * 2.5);- Pre-calculates the flattened, widened puddle configuration: x and z expand by 2.5×, y gets clamped near y=260 to pool in the bowl
this.pos = createVector(x, y, z);- Tracks the actual current position of this vertex in 3D space, updated every frame by physics
this.vel = createVector(0, 0, 0);- Stores the velocity (rate of change) of this vertex, used by verlet integration to accumulate momentum
this.u = u; this.v = v;- UV coordinates for texture mapping—tell the GPU where on the face texture this vertex should display
let stiffness = lerp(0.15, 0.04, meltFactor);- When meltFactor = 0 (solid), stiffness = 0.15; when meltFactor = 1 (liquid), stiffness = 0.04. Lower stiffness = springier, slower recovery
let force = p5.Vector.sub(this.target, this.pos);- Spring physics: calculate the vector pointing from current position toward target, which pulls the vertex back
force.mult(stiffness);- Scale the spring force by stiffness—weaker stiffness produces less forceful pulls, allowing more wobble
this.vel.add(force);- Accumulate the spring force into velocity—this is verlet integration: forces become velocity changes
this.vel.mult(damping);- Apply damping: multiply velocity by a number less than 1 (e.g., 0.82) to gradually slow motion, simulating friction
this.pos.add(this.vel);- Update position: new position = old position + velocity. This is where the smooth motion happens each frame