Recreate the interactive depth portrait effect popularized by modern portfolio websites using Three.js and WebGL.
At this point, the effect can render and react to input. The next step is to make it feel better.
The experience improves through motion smoothing, loading states and small responsive adjustments.
Raw input changes too quickly. If the shader used raw cursor or gyro values directly, the effect would feel nervous.
damped-motion.ts creates a small motion system with two values:
target: where the input wants the motion to go.current: where the scene currently is.Each frame, current moves toward target using damping. This creates a more natural delay and makes the portrait feel less mechanical.
SceneRoot owns the damped motion state.
Inside useFrame, it:
CameraController and ImagePlane both use the smoothed value, which keeps camera motion and shader motion in sync.
The scene depends on texture loading, and the page depends on browser readiness. The loader should wait for both.
The loading system is split into:
use-loading-store: shared readiness state.SceneReadiness: listens to React Three Fiber/Drei loading progress.usePageReadiness: marks the page as ready after the browser load event.LoaderOverlay: displays and exits the loading layer.CountUp: animates the visible percentage.This keeps the first impression controlled instead of showing a half-loaded scene.
useBreakpoint helps adjust the portrait position and scale by viewport size.
The current project uses it inside ImagePlane to move the portrait slightly on mobile and keep the framing pleasant across screen sizes.
Before moving on, the page should:
Next: Finishing