099

Shadow Assembly

£5

Drag a low spotlight freely across the floor to reshape the word or monogram cast by a suspended matte black sculpture. Release to hold its position, then choose Align light to restore the artwork.

advanced
5 more details
webgl-shaderpointer-effectstext-animation3d-transformsclick-toggle
Shadow Assembly - GSAP animation effect preview

This demo reads better at your own screen size than in the frame below:

About this effect

Beveled matte black pieces float against a pale, seamless plaster backdrop. A compact black spotlight casts their shadows into a readable word. The light begins slightly off alignment. Drag the visible lamp freely across the floor to separate the lettering, release to hold the result, and choose Align light to return.

Read the full effect overview

Physically based materials, room reflections and native spotlight shadows give the sculpture and its surroundings a shared lighting response. The installation includes FORM and a geometric monogram as outlined SVG artwork, dedicated desktop and portrait perspectives, four-way keyboard movement, and Aligned / Separated stills for reduced motion or unavailable WebGL.

What's included

10 items
  • Pale seamless plaster setting, a visible low spotlight and dedicated perspective camera framing
  • Stationary beveled matte black solids, lit with physically based materials and a room environment
  • Native three.js spotlight shadows cast by the sculpture's geometry
  • A slightly misaligned opening, with precise assembly when you choose Align light
  • Free two-dimensional lamp dragging across the floor; release, leave or cancel to hold the chosen position, with no hover movement
  • Explicit Align light action with an interruptible return, plus four-way lamp arrow-key controls and Home to align
  • FORM and a geometric monogram supplied as editable outlined SVG artwork
  • Dedicated portrait framing and direct touch dragging
  • Aligned and Separated still views for reduced motion or unavailable WebGL; the opening artwork remains visible without JavaScript
  • Independent instances, container resize handling, context recovery and a documented revert API

Perfect for

4 use cases
  • A studio landing page built around a short word or geometric identity
  • An exhibition page that introduces an artwork through light and shadow
  • A product launch with a distinctive outlined mark
  • An editorial feature about form, material or spatial perception

How it works

1 section

Drag the visible lamp in any direction across the floor while the matte black sculpture stays still. Native spotlight shadows change across the plaster wall as the lamp moves. Release to hold the view, then choose Align light to restore the supplied artwork. GSAP manages the motion preference lifecycle and explicit alignment transition; three.js renders the physically based materials, environment lighting and shadow map. The supplied SVG examples, still images and native controls provide the starting point for your own installation.

Difficulty Advanced
Includes HTML + JS + CSS source, documentation, AI setup prompt, lifetime updates

Lighthouse, as measured

Google Lighthouse on this effect's demo, 15 September 2026. A measurement of the demo as shipped, not a promise for your page.

Accessibility
100
Best practices
100

No performance score, on purpose. That figure depends on how you deploy: your server's compression and caching, your CDN, the connection and the device doing the test, none of which the code controls. The same page can score very differently on two consecutive runs, so measure it where it will live.

Paid effect

Purchase to unlock the code.

Buying Shadow Assembly opens the HTML, CSS and JavaScript source, the full documentation, an AI setup prompt for your editor, and every update we ship to it. Standard license: unlimited personal and commercial projects.

£5 Standard license, unlimited projects

Browse free effects

Documentation

Quick Start

Copy the supplied assets directory into your project and serve the page over HTTP. Artwork is fetched as SVG, so opening the HTML through file:// is insufficient.

1. Add to your HTML <head>:

Code snippet omitted: it ships with the download.

2. Add the installation inside <body>:

Code snippet omitted: it ships with the download.

3. Add before the closing </body> tag:

Code snippet omitted: it ships with the download.

The script automatically mounts every [data-shadow-assembly] root. Keep the still image visible during loading: .is-live replaces it only after a complete canvas frame has drawn. The script probes WebGL before constructing the renderer. Keep the complete supplied assets folder, including vendor/ (the pinned three.js, SVG loader and GSAP dependencies), environment.js and room-pmrem.bin.gz. They load the prefiltered HDR room lighting without generating it on each visit. RoomEnvironment.js remains an automatic fallback when the baked atlas cannot load; BufferGeometryUtils.js and bevel-pieces.js construct the sculpture. Update all import paths together if you relocate the assets.

Using It With Your Own Design

Required markup. Keep one [data-shadow-assembly] root with its own [data-shadow-stage], [data-shadow-source] picture and [data-shadow-lamp] button. The lamp button belongs inside the stage: the script positions it over the physical spotlight and uses it as the drag handle. Keep the artwork select and Align button outside the stage. Preserve the data-shadow-* hooks, native input types, accessible labels and visible focus styles. Each root owns its own controls and canvas.

Demo styling. You can replace the font, control bar, borders and spacing. The global reset, html / body styles, full-screen height and body { overflow: hidden } are demo choices; remove or scope these when embedding the effect in a scrolling page. The sculpture, plaster and fixture materials, environment lighting and spotlight are scene settings in assets/script.js, rather than CSS variables. Desktop and portrait use separately composed perspective camera positions.

Layout dependencies. Give the root a real height and retain its positioned, clipped stage. Keep the canvas and fallback picture filling the stage, the .is-live visibility rules and the rules that switch between live controls and still-view buttons. Avoid hiding the fallback with .has-js. Preserve the absolutely positioned .shadow-assembly__lamp handle, its generous hit area, focus styling and touch-action: none. The surrounding stage can use touch-action: pan-y so the page can scroll away from the handle. Drag cancellation holds the current light position. Keep controls clear of the artwork in both orientations.

Your artwork and still images

Start with assets/form.svg or assets/monogram.svg. Short, broad outlines and compact geometric marks suit the fixed installation. Convert text to outlines in your drawing application, flatten transforms, and check both the aligned and fully separated states at desktop and phone sizes. Long headlines, fine detail and very thin outlines do not suit this composition.

For each custom artwork, supply six matching still images:

  • your-mark-initial-desktop.webp
  • your-mark-initial-mobile.webp
  • your-mark-aligned-desktop.webp
  • your-mark-aligned-mobile.webp
  • your-mark-separated-desktop.webp
  • your-mark-separated-mobile.webp

The supplied desktop stills also include *-initial-desktop-4k.webp, *-aligned-desktop-4k.webp and *-separated-desktop-4k.webp at 3840 × 2160. Add data-still-hd to an artwork option only when you supply these three extra files. The script then selects the 1200px or 3840px image through srcset; custom artwork without this attribute still needs only the six files above. The supplied portrait stills are 780 × 1688.

If you change the opening light coordinates, recapture the initial stills at every supplied resolution so they match the live opening.

Set its option's data-still="assets/your-mark". The script appends the view and viewport suffixes above; it does not generate these images from a custom SVG. Update the initial <picture> sources, image alt, option data-label / data-description, root data-artwork and [data-shadow-label] text to match the initial artwork. The initial select option must match it too. A custom artwork's option must exist before calling setArtwork() so fallback views can select the correct stills.

The stills are essential content: they provide the slightly displaced initial view, the no-JavaScript installation, and both Aligned / Separated reduced-motion or unavailable-WebGL views. Describe the artwork in text independently of the canvas. Changing the SVG alone leaves the supplied fallback images showing the old artwork.

Supported SVG subset

  • A valid SVG with a finite viewBox and positive width and height.
  • Opaque, closed <path> outlines, optionally inside plain <g> groups; <title> and <desc> are allowed.
  • SVG path commands M, L, H, V, C, S, Q, T, A, Z and their relative forms. Close every subpath explicitly with Z.
  • Compound outlines and holes using fill-rule="evenodd" or the default nonzero winding rule.
  • At most 24 paths and 24 filled shapes, a 100,000-character source limit, up to 18,000 triangulated vertices per shape, and at most 80 resulting fragments. Simplify outlines before approaching these limits.

Flatten transforms and expand strokes into filled outlines before export. Text, images, strokes, CSS/style attributes, clipping, masks, filters, transparency, external references and nested SVG documents are outside the contract. SVG fill colours do not change the sculpture material: the sculpture uses one material. Avoid self-intersecting and degenerate contours. This is an outlined-artwork effect, not a live font or arbitrary-image renderer. Check narrow details after beveling and under the native spotlight shadows; fine outlines may need simplifying.

Rejected or unavailable artwork returns the instance to its supplied still view. Supply stills that remain meaningful if the live artwork cannot load.

Options

Set these on the root before mounting. To change an initialization option later, call revert(), change the attribute, then mount again.

Attribute Values Default Description
data-enabled true, false true false uses the supplied still views.
data-artwork SVG URL assets/form.svg Initial outlined artwork; resolved relative to the document URL.
data-strength 01 1 Available floor area around the home position. 0 holds the light at alignment; the default gives the full reachable floor.
data-initial-x World X coordinate -4.622 Opening lamp position, constrained to the reachable floor.
data-initial-z World Z coordinate 11.625 Opening distance from the wall. Set X -5 and Z 12 for an aligned opening.
data-return-duration 0.13 0.9 Align light transition duration in seconds.

The lamp opens at a fixed, slightly displaced position with no entrance animation. There is no autoplay or hover movement. Changing artwork returns to this opening pose; Align light always returns to exact alignment. Drag the visible lamp handle in any direction across the floor. The two floor coordinates move independently; the lamp stays grounded and points toward the artwork. Movement is limited at the wall and visible stage edges so the handle stays reachable. Far from alignment, shadows can stretch, overlap or leave the original word area. Release, pointerleave and cancellation hold the chosen position; Align light explicitly returns it.

Accessibility

  • The lamp handle, Align light button and artwork select are keyboard-operable.
  • Focus the lamp handle and use all four arrow keys to move it in the corresponding screen direction; Home starts Align light. New input interrupts an active alignment transition.
  • Mouse or touch dragging on the lamp handle moves the light freely across the floor in two dimensions. Release, pointerleave, cancellation and lost pointer capture hold the chosen position. Only an explicit Align light action starts the return; stage hover has no effect.
  • Reduced motion opens on the slightly displaced initial still, then offers instant Aligned / Separated still changes with no moving lamp. Preference changes during use tear down or rebuild the live scene.
  • Without WebGL or with a failed WebGL dependency, JavaScript enables the still-view buttons. Without JavaScript, the initial picture and its alternative text remain visible.
  • The canvas is hidden from assistive technology. Keep the meaningful artwork text, image alternatives, labelled controls and visible focus styles in the DOM.
  • Each custom artwork needs its own matching stills and descriptions. An SVG alone cannot satisfy the fallback contract.

Dependencies

Dependency Version Role
GSAP core 3.12+; demo pinned to 3.15.0 Media/lifecycle context and played Align light transitions.
three.js Demo pinned to 0.180.0 Physically based materials, environment lighting and native spotlight shadow mapping.
three.js SVGLoader Same 0.180.0 release Reads the supported outlined SVG artwork.
environment.js and room-pmrem.bin.gz Supplied prefiltered HDR atlas Loads the unchanged room lighting; no runtime filtering on the normal path.
RoomEnvironment and BufferGeometryUtils Supplied 0.180.0 addon / utility extract Fallback room generation and crease-aware geometry normals.
bevel-pieces.js Supplied effect helper Builds the beveled sculpture geometry; retain this local file.

No GSAP plugins or Lenis are required. The rendering code owns its animation-frame loop and continuous input interpolation; GSAP owns the return target and lifecycle.

Already using ES modules? Import your existing three.js module and matching addons, plus the supplied bevel helper. Set the five globals shown in Quick Start before loading assets/script.js: THREE, ShadowSVGLoader, ShadowEnvironment, ShadowCreasedNormals and ShadowBevelPiece. Use one consistent three.js version. The import-map setup avoids the discontinued UMD build. Resolve prepareEnvironment() from the supplied module before assigning ShadowEnvironment. The supplied atlas preserves the original half-float lighting samples; no external HDR image is required.

Browser Support

The live installation requires WebGL 2 and JavaScript with ES module/import-map support. WebGL is probed before renderer creation. Browsers that cannot run the live scene retain the supplied still artwork. Serve assets over HTTP(S) and allow their requests in your site's content security policy.

Performance

The live scene uses a native PCF spotlight shadow map: 4096px on fine pointers, 2048px on touch devices, bounded by the GPU texture limit. Its 64px-per-face room environment is prefiltered once and stored as a losslessly compressed HDR atlas (about 90 KB); visitors skip the environment render passes. Hosts may serve that file with or without a gzip content-encoding header. Older browsers without native decompression, or a missing atlas, use the supplied room generator instead. The black surfaces share standard physically based shading; the luminous lens and its local glow do not require a glass transmission pass or full-screen bloom. The demo uses system fonts and local copies of its pinned libraries, so startup does not wait for an external CDN. It preloads both three.js modules, the SVG loader, initial artwork and lighting data, and includes only the geometry-normal utility it needs. It reuses the probed WebGL context and requests asynchronous material compilation while the matching still remains visible. Shadow rendering adds work beyond the main camera view, and beveled artwork costs more geometry than a flat outline. Drawing pauses after the light settles and when the installation is off-screen. Resize follows the component container. Context loss exposes the still view; restoration preserves the held light position.

Desktop rendering allows up to 8,294,400 pixels (3840 × 2160), with a maximum pixel ratio of 2 and 1.5× supersampling on standard-density screens. Touch rendering retains a 1.9-million-pixel budget and maximum ratio of 1.5. The limits live together in resize(); lower the desktop budget there if a large installation exceeds your target hardware. Responsive 4K stills keep the loading and reduced-motion views sharp without sending the largest image to every viewport.

For expensive custom artwork, simplify the outlines and reduce isolated detail first. A smaller displayed stage also reduces drawing work; lower shadow-map resolution trades edge definition for reduced shadow-rendering cost. data-strength changes travel, not geometry cost. Physical-device phone performance has not been measured. Measure the final page on the devices you need to support; no fixed frame rate is promised.

To rebuild the supplied lighting after editing RoomEnvironment.js in this repository, start the local preview, run bun run build:demos, then bun scripts/bake-shadow-environment.ts http://127.0.0.1:4387, and rebuild the demos. This is an authoring step, never visitor-side work.

The local library copies retain their upstream license notices. After changing the repository CDN pins, run bun scripts/vendor-shadow-assembly.ts to refresh these copies and their source manifest, then rebuild and verify the demo.

Worked examples, the events and programmatic API, and the class reference ship with the download, alongside the full source.

Your cart

Your cart is empty

The Vault £99

The Vault library, plus future additions to the library.