Skip to content
animorio
IT
Back to the library

Seen from here

3D, WebGL, TextWebGL, GSAP, CanvasAdvancedCompressed weight: Demo 8.2 KBGSAP 28 KBThree.js 174 KBFreeDownload the zip

A 3D anamorphosis in the minimal style: thousands of thin black threads hang in space and look like rain from everywhere but three points. From each of those points the threads line up and a word reads: Seen, from, here. The camera travels round the cloud, climbs a little between two points, goes a hair past the next one and settles: the rain clicks into a word that fills with ink from left to right. You can also walk round it by dragging.

The code

A folder that runs on its own: index.html, style.css, script.js and the libraries from a CDN. The React and Vue tabs hold the same demo as a ready component, which switches its animations off when it leaves the page.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Seen from here</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/archivo@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
  <script type="importmap">
    { "imports": {
      "three": "https://cdn.jsdelivr.net/npm/three@0.178.0/build/three.module.min.js"
    } }
  </script>
</head>
<body>
  <!-- an anamorphosis: thousands of thin threads hang in space and look like rain from everywhere but three points.
       From each of those points the threads line up into one word. The camera goes round the cloud and stops on
       each point; drag to look round it yourself. The script cuts the words into threads and places them in 3D -->
  <main class="stage" data-stage>
    <canvas class="canvas" data-canvas aria-hidden="true"></canvas>
    <h1 class="sr" data-sr>Seen from here</h1>
    <div class="dial" aria-hidden="true">
      <svg class="dial__face" viewBox="-20 -20 40 40" data-dial>
        <circle class="dial__ring" r="15" />
        <g data-ticks></g>
        <line class="dial__needle" x1="0" y1="0" x2="0" y2="-15" data-needle />
      </svg>
      <span class="dial__angle" data-angle>0°</span>
    </div>
  </main>

  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
  <script type="module" src="script.js"></script>
</body>
</html>

Playground

Change values, colours and text with the controls and watch the animation respond live. Or write in the code yourself. Then download the folder with your changes.

17 values to change

Or ask Claude Code for it

With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:

Take “Seen from here” from Animorio and fit it into my site

What it does

On a warm white page thousands of thin black dashes grow out of nothing, hanging at different depths: seen from above they look like rain standing still in the air. The camera comes down and travels round the cloud; as it nears the first point the dashes draw closer together, until the camera goes a hair past it, comes back and settles: at that instant the threads line up into horizontal rows and “Seen” reads, hatched like an engraving. The word fills with ink from left to right, the rows get a little thicker and the rest of the cloud fades. It holds, then lets go and the camera moves on, rises above the cloud, goes a third of the way round and settles on the next point: “from”. Then “here”. Meanwhile the threads breathe: they slide slowly back and forth along their line of sight, so the cloud is alive while the word, seen from its point, stays perfectly still. After the last word the camera rises again, the threads shrink back into nothing, and it all starts again. At the bottom a small dial with a vermilion needle says which angle you are looking from; the mark of the point you are standing on lights up. With a mouse the camera shifts a little after the pointer and the word cracks; dragging walks you round the cloud as you like, and letting go settles the camera on a spring onto the nearest word.

How it works

Words cut into threads

Every word is drawn in black on a hidden canvas, at 300 pixels, and read row by row: ROWS rows across the height of a capital, starting from the baseline, so every word has its rows in the same places. Where a row crosses the ink a stroke is born; every stroke is cut into pieces of random length, the threads, which meet one another exactly. All the words sit on the same plane, the one through the middle of the cloud, at the same scale.

The anamorphosis

Every word has its point of view, on a circle round the middle (360 degrees divided by the number of words). Every thread moves along the ray from that point of view to the thread, nearer or further by a random factor (between 1 - SPREAD and 1 + SPREAD), and shrinks or grows by the same factor: from a camera standing exactly on that point the projection does not change, so the word reads intact; from anywhere else the threads are scattered in a deep cloud. A projected segment stays a segment, so the threads stay straight and still meet.

Threads in a shader

All the threads are a single instanced Three.js mesh: a rectangle of four vertices repeated once per thread, with its data (start and end on the plane, depth factor, phase, word, place in the word). The vertex shader works out the position in space, projects both ends and widens the rectangle by WIDTH screen pixels across the thread, so the lines are equally thick at any distance; the fragment shader smooths the edges by hand. Far threads get lighter. The breathing is a sine on the depth factor: it moves the threads along their line of sight and from the right point it does not show.

The camera

The camera sits on a sphere round the middle and always looks there. Between two points a GSAP timeline takes the angle OVERSHOOT degrees past the point with power3.inOut, raising it by RISE degrees and bringing it down meanwhile; then it brings it back onto the exact point in 0.7 seconds. A moment before it stops the word lights up: a sweep from left to right in the shader fills the threads it has passed, thickens them and takes away their fading with distance, while the other words drop to 10 per cent.

The hand

The round is one repeating timeline. A press stops it, the words go back to neutral and dragging moves the angle and the height; letting go works out where the throw would land, picks the nearest point and the camera gets there with back.out. The word lights up as in the round and the timeline goes on from the moment that word is full, with seek: the values match and no jump shows. With a mouse, without pressing, the camera follows the pointer by a little over two degrees.

Values to change

NameFileDefaultWhat it changes
WORDSscript.js['Seen', 'from', 'here']One word for each point of view, 1 to 4: best short and of similar length.
SPREADscript.js0.45How deep the cloud is: 0 is a flat page, 0.7 a very deep cloud.
ROWSscript.js44How many hatch lines across the height of a capital: more rows, a fuller word and more threads.
TRAVELscript.js3.0How many seconds the camera takes from one point of view to the next.
HOLDscript.js1.6How many seconds the camera stays on a word.
RISEscript.js16How many degrees the camera climbs halfway between two points, to show the cloud from above.
OVERSHOOTscript.js1.4How many degrees the camera goes past the point before it comes back and settles.
BREATHscript.js0.05How much the threads breathe along their line of sight: from the right point it does not show, from elsewhere it does.
WIDTHscript.js1.4How thick a thread is, in pixels.

Try them in the Playground

When to use it

For the opening of an art or design exhibition, an architecture studio, a photographer or an agency that talks about points of view; for a campaign that says “look closer”; for the site of an event in a space you walk through. It works with two, three or four words, even a broken-up sentence.

On phones

The same animation with the sizes adapted: the word takes almost the whole width, the camera has a wider angle and the threads get thinner when the rows get closer. A finger drags round the cloud just like the mouse.

With reduced motion

The camera does not move and the threads do not breathe: one word at a time, already set and full of ink, fading into the next. A click starts it over.

Pitfalls

  • WebGL lines are always one device pixel thick (half a pixel on a retina screen) with no soft edges: every thread is a rectangle widened in the vertex shader by WIDTH screen pixels, with half a pixel more at each end so two neighbouring threads leave no gap, and the edge smoothed in the fragment shader.
  • Going back to the round after a drag with a seek to the moment the word lights up, the camera was still coming back from the overshoot and jumped by a degree and a half: now the word lights up outside the timeline, as in the round, and the timeline goes on from the moment the word is already full, where all the values match.
  • On a phone the hatch rows sit two or three pixels apart and at the desktop thickness they turned into a blot: the thickness is worked out on every resize from the distance between the rows on screen.
  • The cloud comes from a seeded random generator, so it is the same every time the page opens, and the poster and the video match the demo.
  • The typeface is awaited with document.fonts.load before the words are cut: with the fallback the threads drew another typeface.

Libraries

Three.js 0.178.0, GSAP 3.15.0, Archivo (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 8.2 KB; the libraries: GSAP 28 KB, Three.js 174 KB.

Testing

Tested on 16 October 2026

  • Desktop: Done
  • Phone: Done
  • Reduced motion: Done
  • Safari on iPhone: To do

License

Use it in your own projects and in your clients’ projects. Do not resell it or redistribute it as is. GSAP stays under its own Standard license. Full license.

All animations