Skip to content
animorio
IT
Back to the library

Raking light

A white plaster rosette on a white wall: at rest you can hardly see it. Where the pointer passes the light comes in low, every leaf and petal casts its shadow, and after a couple of seconds the plaster falls back asleep.

WebGL, HoverWebGL, Canvas, GSAPAdvancedLifetime

The code

A folder that runs on its own: index.html, style.css, script.js and the libraries from a CDN.

The code for this animation is part of lifetime access.

The preview stays open: watch it, slow it down, try it on your phone.

Lifetime access

What it does

A plaster-coloured wall with a bas-relief rosette: a frame with a row of beads, a laurel wreath and a twenty-four-petal flower with a button at its centre. Under straight light the relief casts no shadow and you can barely make it out. Where the pointer passes the light drops and comes from the upper left, raking: slopes facing the light turn white, the others darken, and every leaf and bead casts a lengthening shadow on the ground. The trail fades by itself in a couple of seconds, and two wandering points keep the wall awake even when nobody touches it. The whole wall tilts slightly towards the pointer.

How it works

The relief is a picture

The script draws the rosette on a 2D canvas in shades of grey, where lighter means higher: a ring, 44 beads, 60 leaves, 24 gradient petals and a button. Then it blurs it with three box-blur passes, horizontal and vertical: the height is max(wide blur, 0.6 × narrow blur), so the edges stay soft but the details survive. A third, medium blur slightly darkens the folds. Height and folds go into a 640 × 640 RG16F texture (448 on phones).

The trail

A second 2D canvas, 216 pixels wide and shaped like the wall, holds the trail. Every tick it is covered in black at alpha 1 − 0.85^(dt × 60) and the latest 200 points are redrawn in lighter mode: a radial stamp with alpha (1 − age/120)² × 0.13 and a radius that grows and shrinks with sin(π × life). The pointer adds ⌈30 × distance⌉ points per tick, so even a quick gesture leaves a continuous trail. It draws at most 44 times a second, and the loop stops when the wall leaves the screen or the tab is hidden.

The light

The shader reads the trail with five samples and derives k = (smoothstep(0, 0.3, t) × smoothstep(0, 1, t))^0.7. From k come the depth of the relief (0.6 to 9) and the height of the light (z from 1.4 to 0.45). The normal comes from the height differences between neighbours, and the light is (ambient + n·L) / (ambient + L.z): divided by what the flat plane receives, so the flat wall always keeps the same white and only what has a slope changes. Highlights are not clipped abruptly: they approach white along an exponential curve.

Cast shadows

For each point the shader walks towards the light, twelve steps of 2.5 five-hundred-and-twelfths of the relief, and measures how far the ray passes above the relief, divided by the distance. The smallest value, passed through smoothstep(−0.22, 0.22), says how much the point is in shadow: the penumbra widens with distance, as under a real light. With a high light the ray climbs fast and there is no shadow; with raking light the shadows lengthen.

Values to change

NameFileDefaultWhat it changes
SPEGNEscript.js0.85How much of the trail survives every sixtieth of a second: higher, the plaster stays awake longer.
FORZAscript.js0.13How much each pass of the pointer lights up.
PROFONDOscript.js[0.6, 9]The depth of the relief asleep and awake.
AMBIENTEscript.js0.9The diffuse light: higher, lighter shadows.
rosone()script.jscornice, ghirlanda, fioreThe relief drawing: anything drawn in greys on a canvas becomes plaster, a logo or a word too.
GESSO, --gessoscript.js, style.css#EFECE6The wall colour: change it in both files, or the edge of the canvas shows.

When to use it

For a page that wants to reward exploring: a mark, a work, a symbol you only discover by passing over it. It works with any drawing in greys, a logo too.

On phones

The finger paints the light and a tap lights up its spot. The trail is smaller (128 pixels), drawn 36 times a second at density 1, and the wandering points light up more, because a finger does not pass all the time. Inside an iframe a vertical finger stays with the page.

With reduced motion

No trail and no wandering points: the relief stays still under a half-low light, readable. A tap takes the light from straight to raking, and back, in six tenths of a second.

Pitfalls

  • The flat wall must keep its colour under any light: divide by the light the plane would receive, or every change of light makes the whole background flash.
  • Upload the height texture at 16 bits (RG16F): at 8 bits gentle slopes turn into steps, and raking light shows every one of them.
  • The trail’s fading and the points’ age must be multiplied by dt × 60: if the loop slows down, the trail must last the same seconds, not the same frames.
  • Many pixels can pass between one pointer event and the next: fill the stroke with intermediate points, or the trail becomes a row of blots.

Libraries

GSAP 3.15.0, Mona Sans (font variabile) 5.3.0.

Weight: 21 KB of our own files.

Testing

Tested on 27 September 2026

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

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