Transforming Images into Dynamic Ribbons with Three.js

Sep 05, 2026 401 views

Unwoven is an experimental project that redefines the way we perceive image presentations on the web. Instead of traditional sliders that simply move images around, this concept disassembles images into individual ribbons that animate across the screen. As they glide, these ribbons create a mesmerizing effect of weaving and unweaving, drawing in viewers with their dynamic motion. This shift from static to dynamic image representation speaks volumes about the evolving nature of web design and user engagement.

Unwoven Demo

Editor’s Note: In showcasing this work, the Three.js community is reminding us of the capabilities of WebGL to breathe life into static visuals. Clément Grellier's "Unwoven" is an exquisite demonstration of how multiple graphical elements can become a cohesive visual narrative, stirring a sense of wonder. The blend of art and technology here isn't just aesthetic; it's a testament to how web capabilities have expanded our creative horizons.

🧵 Join the movement! The inaugural Three.js Conference in Paris is on the horizon, featuring talks and networking opportunities. Use code CODROPS for 15% off your ticket!

The Illusion of Regularity

At its core, Unwoven features a continuous series of image cards, each designed as a stack of 26 horizontal ribbons. When viewed from the center of the screen, the ribbons create the illusion of a regular photograph. However, as these cards begin to move, the illusion breaks, and the ribbons diverge at various speeds, adding an intricate fluttering motion to the display. This interplay between order and chaos adds depth to the viewer's experience and challenges preconceived notions of how images should behave on a screen.

The magic behind this effect is simple yet profound: it involves Three.js for rendering, GSAP for animations, and a minimal setup consisting of just two shaders and a single HTML file. This minimalist architecture is often misunderstood — a more complex system isn’t always superior. Here, the simplicity of the code enhances the user experience by allowing for quick loading times and smooth animations, something many developers might overlook while chasing advanced features.

Decoding the Mechanism

Each of those 26 ribbons functions independently, incorporating its own geometry created from two vertices in height and 21 in width. This approach allows for a strikingly authentic appearance of a cohesive image as the ribbons come together. It’s a lesson in how micro-level engineering can yield macro-level results. The animation relies on random numbers derived from each ribbon's index, which influences how quickly a ribbon escapes the viewport, how far it drifts, and how dynamic its fluttering movement appears.

Step-by-Step Animation Creation

The first step in creating this ribbon effect is to assign unique vertices to each thread instead of defaulting to a single plane with subdivisions. Each ribbon should maintain its own vertices to avoid the unrealistic stretching that occurs when shared vertices are used. This sets the stage for an engaging visual experience that keeps the user glued to the screen.

for (let t = 0; t < 26; t++) {
  for (let row = 0; row < 2; row++) {
    const v = (t + row) / 26;
    for (let c = 0; c <= segments; c++) {
      const u = c / segments;
      position.push((u - 0.5) * width, (v - 0.5) * height, 0);
      uv.push(u, v);
      rim.push(row === 0 ? -1 : 1);
      threadId.push(t);
    }
  }
}

Next, each thread must glide across the screen at varied speeds. This effect is achieved by calculating proximity to the edge through a variable dubbed tear, which triggers a shift in horizontal position based on its distance from the center. This attention to detail is where the experience comes alive: the viewer’s perception is manipulated not just by what they see, but how it moves.

float tear = max(
  1.0 - smoothstep(-halfWidth, -halfWidth + zone, x),
  smoothstep(halfWidth - zone, halfWidth, x)
);

Creating Gaps and Fading Effects

As the ribbons diverge, they still initially appear as solid rectangles. To create the illusion of separation, the edges of each thread are obscured in the fragment shader, modifying their visibility based on the distance to the screen's edge. This sophisticated layering in the visual representation makes the experience richer and more nuanced — you’re not just looking; you’re experiencing.

float edge  = abs(rim);
float width = mix(0.8, 0.2, tear);
float alpha = 1.0 - smoothstep(width, width + 0.06, edge);

By adjusting parameters like 60.0 + rnd * 420.0, developers can influence the stagger of the threads, while modifications to the number of ribbons can signify varying textures, from paper to hair-like appearances. The versatility here showcases the potential for web design to transcend mere function and enter the realm of artistry.

Implications and Future Outlook

What this means for you, as a developer or designer, is that there’s a wealth of opportunity in rethinking traditional image presentations. If you're working in this space, consider how such visual techniques could play into your projects — especially in an age where user attention is paramount. The Unwoven project is more than just a visual spectacle; it symbolizes a shift towards dynamic, engaging content that can enhance user experience.

Looking forward, we can expect more projects that push the boundaries of what's possible with web graphics and animations. The responsiveness and fluidity shown in Unwoven could lead to changes in how content is displayed across platforms — not just on desktops but on mobile devices as well. This trend signals a move toward a more interactive web, where viewers are active participants rather than passive consumers. And that’s a welcome evolution.

Source: Clément Grellier · tympanus.net

Comments

Sign in to comment.
No comments yet. Be the first to comment.

Related Articles

Building an Infinite Loom: Unravelling Images into Thread...