Face Points
A face mesh begins as landmark points. Each point marks a stable semantic place on the face: a mouth corner, eyelid edge, brow ridge, nose bridge, cheek contour, or jaw point.
Most browser pipelines store those points in normalized image space so the same data works for any render size.
x: 0.0 left -> 1.0 right
y: 0.0 top -> 1.0 bottom
z: relative depth
The critical rule is point-index identity. Point 61 in expression A and point 61 in expression B must mean the same anatomical point, or the morph will twist.
From Points To Triangles
A renderer cannot draw a cloud of points as a face surface. It needs triangles, and the same triangle list should be reused for every expression.
Voronoi And Delaunay Intuition
A Voronoi diagram answers: which landmark owns each patch of nearby image space? Its dual, the Delaunay triangulation, connects landmarks so triangle edges tend to avoid long skinny slivers.
That matters for faces because skinny triangles stretch texture aggressively. Delaunay-style connectivity gives better local neighborhoods for cheeks, lips, and eyes.
Morphing Positions
The simplest morph is linear interpolation. At time t, each vertex is somewhere between expression A and expression B.
P(t) = A + (B - A) * t
When every landmark index is consistent, the whole face appears to change expression. When an index is wrong, a lip vertex may jump into an eyelid or a cheek can fold through the mouth.
Texture Coordinates And Dual UVs
Geometry motion is only half the trick. The shader also needs to sample each source texture at the correct landmark coordinates. The face viewer keeps two UV attributes: one for expression A and one for expression B.
color = mix(textureA(uvA), textureB(uvB), t)
This is why the texture follows the face points instead of sliding over a static rectangle. The destination mouth texture is sampled from the destination mouth coordinates, even while the geometry is halfway through the morph.
Transforms
Normalized image points must be transformed into the coordinate system used by the canvas or Three.js scene. A common mapping centers the face and flips the Y axis so screen coordinates become world coordinates.
world_x = (x - 0.5) * 2
world_y = -(y - 0.5) * 2
world_z = z * depth_scale
Scale, rotation, and translation are then safe to expose as controls because they operate on the mesh after the landmark topology has been built.
Debugging Checklist
- Draw the points before drawing triangles.
- Toggle wireframe to inspect topology during the blend.
- Check the same landmark index on both expressions.
- Verify UVs against both source images, not just the current geometry.
- Keep privacy boundaries explicit: this is a morphing demo, not identity verification.
Open the live surface at face.ragbaz.cc, or read the implementation handoff in the Atlas experiment notes.