REFERENCE
- Gallery page: https://magpui.com/en/i/falling-yarn-threads-that-tangle-ca3f16
- First frame (image): https://dnioievkvxejjbxgwvko.supabase.co/storage/v1/object/public/media/falling-yarn-threads-that-tangle-ca3f16/shot.jpg
- Clip (mp4): https://dnioievkvxejjbxgwvko.supabase.co/storage/v1/object/public/media/falling-yarn-threads-that-tangle-ca3f16/clip.mp4
- Original post: https://x.com/mrdoob/status/2117978936101728412 by @mrdoob
Before building, download the first frame and look at it. If ffmpeg is available, download the clip and extract frames (ffmpeg -i clip.mp4 -vf fps=2 frame-%02d.png), then study the motion frame by frame. Match what you see; the text below describes it, the frames are the truth.
BUILD: A full-screen interactive 3D scene where five thick strands of twisted wool yarn hang from above the top edge of the viewport and slowly feed downward onto an endless pale floor. As each strand reaches the ground it bends, slides and coils on itself, and the strands pile into one soft ring-shaped tangle that keeps growing and overlapping. The user can drag any strand to pull it through the pile, drag the background to orbit, scroll to zoom, press Space to pause, R to reset and C to shuffle the palette. It should feel calm, handmade and physical, like watching yarn spill out of a basket in slow motion.
DESIGN SYSTEM: Full-bleed canvas with no frame. Tiny monospace caption top-left (11px, uppercase, letter-spacing 0.08em, #4A4540) with a smaller hint line below it ("Drag a thread · Space to pause", 10px, 70% opacity), and a wide translucent "Settings" bar top-right (#D8D0C4 at 80%, 2px radius, 10px monospace). Background: warm off-white studio sweep, #E7DFD2 at the top fading to #DDD6CB at the horizon, floor #E2DBD0 with a soft fog band so floor and wall meet without a hard line. Yarn colors: sage green #3E8A6E, dusty lilac #C9A9B9, mustard #E2B43A, cream #E9E1C8, coral #EE6A45. Each strand is about 1.6% of the viewport width thick, with a visible 3-ply twist (helical grooves, one turn every ~3 diameters), slight fuzz highlights and matte roughness 0.8. Soft ambient occlusion and contact shadows in #9C9284 at 30% opacity where strands touch the floor and each other.
MOTION: Feed: each strand descends continuously at about 0.25 units per second, with a staggered start of 300-600ms between strands so they land at different times. Landing: the tip touches the floor, bends 90 degrees and slides forward, then the falling length buckles into loops and figure-eights with a natural coiling bias (each strand curls clockwise, its neighbour counter-clockwise). Over roughly 20 seconds the coils from all five strands merge into a ring-shaped pile about 60% of the viewport wide. Settling: velocity damping so loops stop wobbling within ~800ms after landing. Drag: grabbing a strand attaches a spring to the nearest particle (stiffness high, damping 0.2); releasing lets it fall back with gravity. Pause freezes the simulation; Reset fades the pile out over 500ms cubic-bezier(0.4, 0, 0.2, 1) and restarts the feed. Palette change cross-fades colors over 400ms.
SECTIONS: Parts and states: strands hanging (start), feeding, first contact, coiling pile, pile at rest while feed continues, dragging a strand (cursor grab), paused, reset. Settings panel (collapsed by default) exposes feed speed, strand count, thickness and twist.
IMPLEMENTATION: Three.js with the WebGPU renderer and TSL compute (fall back to WebGL2 with a CPU solver at lower counts). Simulate each strand as a chain of particles with position-based dynamics: distance constraints between neighbours, bending constraints for stiffness, a floor plane collision with friction, and self/inter-strand collisions via a uniform spatial hash. Render each strand as an instanced tube or swept mesh along the particle chain, with the twist done in the shader by rotating a 3-lobe cross-section along the arc length plus a normal map for fibers. Add a new particle at the top each step to emulate the feed and cap total particles (e.g. 6k) by freezing the oldest settled loops. Use raycasting against the particle chain for dragging. prefers-reduced-motion: show the finished tangle at rest and only animate on drag. The canvas has an aria-label describing the scene and is focusable; Space, R and C work from the keyboard; the Settings bar is a real button with a visible focus ring. Cap device pixel ratio at 2 and pause rendering when the tab is hidden.
ADAPT TO THE PROJECT: Reuse the project's existing design tokens, fonts and UI components for the caption and settings panel, map the yarn colors to the closest existing tokens, and never copy logos, brand names, photos or illustrations from the original; use placeholders.
SUCCESS CHECK:
- Five twisted strands visibly fall, bend on contact and coil into one overlapping pile without passing through each other or the floor.
- Dragging a strand pulls it through the pile and it falls back naturally when released; Space pauses and R resets.
- With prefers-reduced-motion the pile is static until dragged, and otherwise the scene stays above 55fps on a mid-range laptop.