...loading...
Canvas size:
Grid:
Haunted Hover — Stage
...loading...
Canvas size:
Grid:
Stage 4 makes the ghost come alive. Two SWSinusoid instances drive
a gentle horizontal drift and a subtle vertical hover โ no new classes needed,
just a few lines added to draw().
How It Works
The sketch tracks a base position (ghostBaseX,
ghostBaseY) separate from the instantaneous animated position.
Each frame, when the ghost is not being dragged:
const etime = millis() / 1000; // elapsed seconds
ghost.setPosition(
ghostBaseX + hSin.getValue(etime),
ghostBaseY + vSin.getValue(etime)
);
Because setPosition() calls _build() internally,
every child โ body, toes, and eyes โ follows automatically.
Sinusoid Parameters
hSin) — amplitude 0.20 user units, period 5 seconds, phase offset 1.5 s so it starts mid-cycle.vSin) — amplitude 0.30 user units, period 3 seconds, phase 0.Drag + Animation Coexistence
The ghost remains fully draggable while animating. When the mouse is released, the base position is resynced:
ghostBaseX = ghost.headCenter.x - hSin.getValue(etime); ghostBaseY = ghost.headCenter.y - vSin.getValue(etime);
This removes the current sinusoid offset from the drop position so the ghost floats smoothly from wherever it lands instead of snapping back.
Controls
What Changed in the Sketch
Compared to Stage 3 (single-ghost variant), the sketch gains:
hSin and vSin โ two SWSinusoid instances constructed in setup()ghostBaseX / ghostBaseY โ base position variablesdraw() to apply the animation when not draggingmouseReleased()keyPressed()The Road Ahead
Stage 5 will refine the SWGhost API and produce a class reference page.
A future lookAt(targetX, targetY) method will aim both pupils
at a point simultaneously.