Study 03 / Elastic Fabric

A surface made from shared displacement

Elastic Fabric is not a cloth or constraint solver. Its 195 or 247 points are tethered home, coupled through four-neighbor displacement differences, and strongly damped.

How much motion should neighboring elements inherit from one another?

Open the live system
Elastic Fabric tension-loom diagramA dashed rest grid sits behind a solid locally deformed grid. Tether lines connect selected intersections to their home positions, and a load arrow marks the displaced patch.
Home
8.5
Coupling
3.8
Damp
4.7
Field
220px
Dashed marks show home positions; solid lines show a schematic screen-space deformation. Every drawn edge also identifies a neighbor relation used by the force calculation.
Published
Jul 12, 2026
Updated
Jul 12, 2026
Reading
10 min
On this page
  1. The visible seam
  2. Lattice before material
  3. Shared displacement
  4. A patch, not a point
  5. Settle before spectacle
  6. One pass, then stillness
  7. Where connection earns motion

01 / Elastic Fabric / The visible seam

The lines reveal who is coupled.

A line in Elastic Fabric is more than rendering. Every horizontal and vertical seam joins two nodes whose displacements are compared in the force pass. The picture and the topology agree: if an edge is absent from the drawing, that neighbor relationship is also absent from the model.

Connection does not come from preserving the visible length of that line. Each node measures how far it has moved from home, then compares that displacement with up to four neighbors. A local error becomes a nearby force on the next frame, which is enough to make a flat grid read as one surface.

The emerald brightness is art direction, not a stress map. Line alpha increases by column and every fifth point is cyan; neither treatment reports force, strain, or priority. The model is truthful about adjacency while the palette remains editorial.

The line reveals adjacency. The force decides whether adjacency feels material.

02 / Lattice before material

Two fixed cuts define the surface.

At 760px and above, the scene uses a 19-by-13 lattice: 247 points and 462 drawn edges. Below that breakpoint it becomes 13-by-15: 195 points and 362 edges. The mobile cut trades horizontal resolution for more rows, keeping the mesh legible in portrait orientation rather than merely shrinking the desktop topology.

Two fixed lattice cuts bound the workload
Desktop cut
19 × 13247 points462 edges
Mobile cut
13 × 15195 points362 edges

Horizontal span: min(78vw, 1040px) · vertical span: min(62vh, 620px)

The active span is capped at 78% of viewport width or 1,040px, and 62% of viewport height or 620px. Seed zero begins exactly on that regular screen-space grid with zero velocity; non-zero seeds add a small, edge-tapered initial displacement without changing topology. Resize reconstructs the transient state from the current seed, so dimensions remain coherent at the cost of losing the current deformation.

This is not a cloth or distance-constraint solver. It has no diagonal fibers, shear or bending links, edge pins, collisions, self-intersection handling, rest-length projection, or iterative constraint passes. Edge nodes simply have fewer neighbors; every point, including the interior, has its own soft route home.

Four-neighbor displacement stencilA center intersection is linked to left, right, top, and bottom neighbors. Diagonal cells are crosshatched to show that they do not participate in the force sum.
Included
left / right / up / down
Absent
diagonal / shear / bend
The calculation compares displacement, not current edge length. Crosshatched diagonals are outside the topology.

03 / Shared displacement

A surface is made from disagreement about home.

Let uᵢ = pᵢ − oᵢ be node i’s displacement from its origin. The first force term pulls that displacement toward zero with coefficient 8.5. The second compares it with the displacement of each orthogonal neighbor, multiplying their difference by 3.8. Written compactly, the neighbor term is a graph Laplacian over displacement.

The force model before pointer input
u_i = position_i - origin_i

F_i = -8.5 u_i + 3.8 * sum(u_j - u_i)
                         j in N(i)

A uniform offset gives neighboring nodes almost nothing to disagree about, so the origin tether dominates. A sharp local dent creates large displacement differences and therefore stronger transfer. Alternating offsets produce the strongest smoothing response. The lattice’s apparent material quality comes from those spatial modes, not from the drawn edge behaving like a rigid thread.

There is no explicit mass value. Force is integrated directly into acceleration, which gives every node the same implicit unit mass. That omission is useful to state: changing “mass” is not one of the current design controls, and visual resistance comes from the stiffness coefficients and velocity loss instead.

The coefficients above are the intensity-50 baseline. Across the valid 0–100 range, origin stiffness stays within 6.5–10.5, neighbor coupling within 2.6–5.0, damping within 5.8–3.6, and pressed-pointer strength within 24–60. These are bounded product settings; grid dimensions, force buffers, and the integration cap remain renderer internals.

04 / A patch, not a point

Pressing loads a neighborhood.

Fabric does not select or pin one vertex. While pressed, every node within 220px receives a force toward the pointer. The coefficient is 42, and a quadratic falloff makes the center respond strongly while the outer boundary approaches zero without a hard ring.

A quadratic load comb reaches zero at 220px
w(d) = (1 − d / 220)²
Pressed pointer contribution for distance d < 220
weight = (1 - distance / 220)^2
F_pointer = 42 * weight * (pointer - point)

Hover is a different input. It adds only a vertical sine force with amplitude 12, a period of about 3.14 seconds, and an x-phase wavelength of about 524px. Pointer speed does not affect either mode. Releasing a mouse removes the pressed force but leaves the weaker hover wave at the cursor; releasing touch now ends the active field because touch has no hover contract.

Links, controls, and reading copy are excluded. On touch screens, vertical movement remains available to page scrolling through touch-action: pan-y, so a vertical drag can be cancelled by browser scroll. The scene is ambient direct manipulation, not a precision grab handle.

05 / Settle before spectacle

Recovery is part of the material.

All forces are accumulated before any point moves, preventing traversal order from giving later nodes newer neighbor positions. The integrator then updates velocity, applies exponential loss, and advances position. This is one semi-implicit Euler pass rather than an iterative constraint solve.

The reviewed state update
delta = min(delta, 0.028)
velocity = (velocity + force * delta) * exp(-4.7 * delta)
position = position + velocity * delta

Without continuing force, velocity has a half-life of about 147ms. The origin and neighbor springs continue acting, so full recovery depends on the coupled mode, not that half-life alone. There is also no fixed substep schedule: the 28ms cap prevents a delayed frame from becoming one unstable leap, but discarding excess time makes the scene briefly slow after a stall.

Strong loss is the production choice. A looser lattice would advertise every rebound and make the interface wait for its own flourish. Here the deformation travels far enough to establish connection, then the grid’s original alignment becomes the dominant visual again.

06 / One pass, then stillness

The frame budget is a construction schedule.

Per-frame work is O(points + edges). The desktop force pass checks 988 candidate neighbor slots and applies 924 valid directed contributions before drawing 462 seams and 247 points. Mobile checks 780 slots, applies 724 contributions, draws 362 seams, and fills 195 points. Reusable typed force buffers avoid allocating two new arrays on every frame, while the four neighbor offsets are shared instead of rebuilding a neighbor list per node.

One force pass, one integration pass, one drawing pass
  1. 01Force pass

    Home, four-neighbor, then pointer contribution

  2. 02Integrate

    Semi-implicit Euler with a 28ms local cap

  3. 03Draw

    362 or 462 seams plus every lattice point

  4. 04Wash

    A time-corrected 0.52-at-60Hz visual clear

The Canvas wash is calibrated to alpha 0.52 at the 60Hz reference and converted from delta, keeping faint visual persistence closer across refresh rates. State integration is capped separately at 28ms. The shared Canvas still caps DPR at 2 and cancels its animation frame when the document is hidden or the atlas is offscreen.

With prefers-reduced-motion: reduce, the loop never starts. Every point is placed into a deliberate diagonal wave using13 × sin(0.55 × column + 0.28 × row) at seed zero; non-zero seeds alter its bounded phase and amplitude. Horizontal offset is 36% of that value. The still renderer does not rewrite live positions or velocity. Leaving reduced-motion mode therefore resumes the preserved lattice without having advanced it or injecting a hidden static deformation into the solver.

Reduced-motion lattice specimenA static diagonal deformation made from the same sine equation as the reduced-motion fallback.
The still is authored from 13 × sin(0.55c + 0.28r); it is not a frozen live frame.

The still is not a paused live mesh. It is a separate composition with at most about ±4.68px of horizontal and ±13px of vertical displacement. The Canvas remains aria-hidden, while explanation and navigation remain available in semantic HTML. The shared Pause / Resume transport now freezes the live lattice, its velocities, and the active phase clock without replacing them with this authored reduced-motion specimen.

07 / Where connection earns motion

Let neighbors inherit motion only when grouping matters.

Coupled response can clarify that adjacent elements belong to one object: a grouped drag affordance, a shared layout transition, or a surface whose local change should be noticed beyond one control. It earns attention when transferred motion explains structure and recovery finishes before the next decision.

  1. 01 / Load

    Use direct manipulation in an open, bounded region.

  2. 02 / Transfer

    Let adjacent elements inherit enough offset to reveal grouping.

  3. 03 / Recovery

    Return alignment quickly enough that rebound never becomes the task.

  4. 04 / Reject

    Keep decorative lattice motion away from reading, forms, and precise status.

Do not use this model when edge length, collision, folding, tearing, or pinned boundaries carry meaning. Those requirements need a real constraint system and stronger testability, not extra stiffness in a visual approximation. A CSS transition or shared-element transform is also better when only one or two elements need continuity.

Connection is not the line between two points. It is the amount of displacement those points are asked to share. If shared displacement does not explain the interface, the most disciplined coupling coefficient is zero.

Method

References and implementation sources

This article documents the live implementation running on this site. These primary standards and publication records were used to verify API behavior and technical terminology.

  • HTML Canvas 2D ContextWHATWG HTML Living Standard

    The rendering contract used to draw the bounded point-and-edge lattice.

  • Animation framesWHATWG HTML Living Standard

    The requestAnimationFrame processing model behind the time-normalized solver loop.

  • Pointer Events Level 3W3C

    Pointer capture and cancellation behavior for pulling and releasing the mesh.