Under the hood
How a photo learns to wobble.
No keyframes, no video file, no AI. Just a mesh and a very old equation.
The 30-second version
A photo is turned into a grid of points. Each point knows two things: where it belongs, and where it currently is. When you pull, you're moving a handful of points. Their neighbours follow, their neighbours follow them, and the whole thing behaves like a piece of soft rubber.
Let go, and the points pull themselves home. The overshoot is the wobble.
What's actually moving
The mesh is about 10,000 points (72×72 plus a margin). Each point stores a displacement — not a position, but “how far from home am I right now.”
Working with displacement is the trick. If you simulate positions instead, the mesh fights its own rest lengths and starts to buckle. With displacement, the resting state is simply zero, and the maths stays calm.
Why it feels like skin and not a flag
The points obey a damped wave equation:
ü = c²∇²u − ω²u − γu̇ + ν∇²u̇
c²∇²u— skin pulls on skin. This is what makes a pull travel outward.−ω²u— skin is attached to the skull underneath. The further it goes, the harder it pulls back.−γu̇— friction. The wobble dies down instead of ringing forever.ν∇²u̇— the fleshy part. Fine ripples are swallowed immediately; big slow wobbles survive. This is the difference between jelly and a rubber sheet.
Then the lighting is faked from the same deformation: stretched skin lifts toward the viewer, bunched skin ridges up, and the slope of that pretend height map becomes a surface normal. Taut skin goes pale; pinched skin goes dark and a little red.
Why the head can leave the circle
The circular frame is applied in the photo's own coordinates, not on screen. So the question is never “is this pixel inside the circle right now?” but “did this piece of skin start inside the circle?”
Which means: skin that began inside the frame is allowed to leave it. That's why a pulled cheek can end up outside the circle, and why the head can pop over the top edge.
Why the eyes move but the face doesn't
The expressions are pictures — a dozen of them, drawn or photographed, all lined up to the pixel. Swapping one for another is a crossfade between two textures, which is cheap but coarse: you can't crossfade your way between fifteen directions of gaze.
So the eyes aren't part of the face. They live on their own little sheet, one cell per glance, and each cell holds two patches: the left eye and the right. Outside those two rectangles every cell is the resting face, to the pixel. The shader swaps the patches — which means a glance costs nothing, moves nothing else, and can change fifteen times a second without a seam.
Nothing about this is a model or a rig. It's two textures and a rectangle.
What it isn't
- Not a filter. Nothing is pre-rendered, and there are no keyframes.
- Not a video. The file size doesn't grow with the length of the wobble.
- Not AI. No model, no inference, no cloud. It's arithmetic, in your browser, at 60 frames a second.
Add it to your site
The engine is one dependency-free script, around 26KB gzipped. It renders
with a single WebGL draw call and stops simulating when nothing is moving, so it
isn't burning a phone battery in a background tab. It respects
prefers-reduced-motion and falls back to a still image when WebGL isn't
available.
Questions from people who read this far
Does it need WebGL?
Yes, for the live version. Every browser from the last decade has it. Without it, you get a photo and an apology.
Why is it only 26KB?
Because it doesn't ship a 3D library. It's about 600 lines of maths and one shader.
Can I use it commercially?
Yes, with the embed licence. One site per licence, no resale, no rebranding.
Does it work with a photo of a pet?
Yes. Anything with a roughly face-shaped mass. The physics doesn't know what a face is.