Live and interactive at romanjahandideh.com. Try it.
This project is a fully procedural, anatomically-inspired eye system built as the centrepiece of my personal portfolio homepage. Every behaviour was hand-crafted in pure JavaScript, no canvas, no WebGL, no third-party libraries, running inside a single requestAnimationFrame loop that drives everything in real time.
The pupil follows your cursor with smooth interpolation, layered with subtle micro-saccades: involuntary tremor-like offsets that mimic the way a real human eye never fully holds still. The closer your cursor gets, the more the pupil constricts — a direct reference to the pupillary light and proximity reflex. Move fast, and it reacts to speed. Move away, and it slowly relaxes.
Click anywhere on the page, and the eye blinks — with a randomised double-blink chance to keep it feeling organic rather than mechanical. When the page is left idle, it enters an autonomous blink cycle, firing every one to five seconds at natural, randomised intervals. The eyelid motion is split into separate top and bottom shutters, weighted asymmetrically, the way real eyelids move.
Scroll the mouse wheel and the iris ring rotates with physics-based momentum — it accelerates on input and decelerates with damping, never snapping or teleporting. On mobile devices, the entire gaze system switches to gyroscope input: the eye tracks the physical tilt of the device through the DeviceOrientation API, with screen-rotation compensation so it stays correct in landscape mode.
The system is built entirely on CSS custom properties injected at the root level. Position, scale, lid state, rotation, and parallax offsets are all written as variables per frame — keeping the rendering layer in CSS where the browser can optimise it, while JavaScript handles only the logic.
Stack: HTML · CSS Custom Properties · Vanilla JavaScript
Interactions: Mouse tracking · Click blink · Proximity dilation · Scroll spin · Idle cycle · Mobile gyroscope