# Silk Background

> A GSAP and WebGL silk background: a sheet of satin drifts in slow folds behind your content, the pointer moves the light across its sheen, and press and hold sends ripples out under the cloth.

Canonical: https://gsapvault.com/effects/silk-background
Live demo: https://gsapvault.com/demos/silk-background/index.html

| Property | Value |
|----------|-------|
| Type | effect |
| Tier | paid |
| Price | £10 |
| Difficulty | advanced |
| Plugins | Core GSAP only |
| Techniques | webgl-shader, organic-motion, pointer-tracking, ambient |
| Uses Lenis | No |

## Lighthouse, as measured

Google Lighthouse on the demo, 8 October 2026. A measurement of the demo as shipped, not a promise for your page.

| Category | Score |
|----------|-------|
| Accessibility | 100 |
| Best practices | 100 |

No performance score is published: it depends on the deployment (server compression, caching, CDN, connection and device) rather than on the code, so measure it where it will live.

## Overview

A background that reads as a material rather than a gradient: a sheet of deep emerald satin lying loose behind your content. Long folds drift, bend, merge and part, slowly enough that the motion is felt more than watched. The light on it behaves like light on real satin: highlights stretch along the weave into long, bright streaks on the crowns of the folds, and the valleys fall into soft shadow, so even a still frame looks like cloth.

The pointer is the light. Move it and the sheen rakes across the folds, catching ridges you could not see a moment before; the light follows with a little weight rather than snapping to the cursor. Press and hold and a ripple lifts from the pointer and rolls outward across the whole sheet, fading as it travels, and while you hold, another follows every second or so. Presses stack, so quick clicks send overlapping rings. Let go and the cloth settles back into its slow drift in under a second.

Your headline sits on a quieter stretch of cloth: behind any element you mark, the folds flatten and the sheen dims, so body copy holds its contrast even at the brightest moment. One colour drives the whole material. Change it in CSS and the highlights and shadows follow, so a navy, oxblood or charcoal silk still looks lit. Visitors without WebGL or JavaScript, and anyone who prefers reduced motion, see a baked still of the satin at its best light.

## Features

- Real satin in WebGL: slow folds with long, anisotropic highlights along the weave and soft shadow in the valleys
- The pointer is the light: its sheen rakes across the folds and follows with weight
- Press and hold for ripples: they lift from the pointer and roll outward, fading as they travel
- Repeated presses stack into overlapping rings; release settles in under a second
- Calm zones: behind marked content the folds flatten and the sheen dims, keeping body copy readable at peak brightness
- One CSS colour drives the material; sheen and shadow are derived from it unless you set them
- Fold size, flow speed and overall intensity each set from one data attribute
- Touch: the light drifts on its own between touches, drag to move it, tap to send a ripple
- Keyboard: arrow keys move the light, Space or Enter sends a ripple
- Works in any positioned container, from a full-screen hero to a single card
- Pauses off screen and in hidden tabs, and stops drawing entirely when flow and drift are switched off
- Baked still for reduced motion, no WebGL and no JavaScript
- Small API: set the light, send a ripple, pause and play

## Use Cases

- Fashion, bedding, fragrance and jewellery landing pages that want a tactile, premium ground
- Hotel, spa and hospitality heroes
- Product launches and waitlist pages with one line of type centre stage
- Event invitations and gala pages
- Portfolio and studio sites that want a background with texture rather than a flat colour

## Vibe-Code Ready Setup

This effect includes `START-HERE-AI.md`, a product-specific copy-paste setup prompt for Cursor, Claude Code, ChatGPT, GitHub Copilot, Windsurf, and other coding assistants. It tells the assistant to inspect the existing stack, integrate the supplied files, preserve the design, scope selectors, retain accessibility and responsive behaviour, add framework-appropriate GSAP cleanup, and report what it tested.

[How AI-assisted setup works](https://gsapvault.com/vibe-coding)

## How It Works

A three.js WebGL shader draws the satin each frame as a lit surface. The folds are long ridges whose shape is pushed around by slow noise, so they never repeat. The surface is shaded with an anisotropic highlight, stretched along the weave and tight across it, which is what makes satin look like satin, plus soft occlusion in the valleys and a fine dither so the dark folds never band.

GSAP plays the timed moments: the reveal as the cloth fades up out of its own shadow, each ripple's lift, travel and decay, and the swell under a held press with its release. GSAP's media queries switch between pointer, touch and reduced-motion behaviour, and its context tears everything down. A frame loop on GSAP's ticker owns the continuous motion: the slow drift of the folds and the light's weighted chase of the pointer.

The background stops drawing when it leaves the screen or the tab is hidden, and goes fully idle once settled when flow and drift are switched off.

## Documentation

How this effect works and how it goes into a page. The reference you use once you own the files (worked examples, events, the programmatic API, the class list) ships with the download.

### Quick Start

**1. Add to your HTML `<head>`:**

_Code snippet omitted: it ships with the download._

The inline script is not optional. The layer carries a baked still of the satin for visitors without JavaScript or WebGL and for anyone who prefers reduced motion. Without this probe every visitor would download that image and see it for the half second three.js takes to arrive, then watch it swap for the canvas. The probe runs before first paint and stamps `html.gl` when a canvas is coming; the stylesheet drops the still under that class, so it is never even requested. The effect removes the class again if it cannot build a renderer after all.

**2. Put the layer inside any positioned container, before your content:**

_Code snippet omitted: it ships with the download._

`data-silk-calm` marks content that should sit on a quieter stretch of cloth. Put it on the box that holds your text (up to four elements per layer).

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

_Code snippet omitted: it ships with the download._

**Why the import map rather than a plain `<script src>` for three.js:** three ships as ES modules only, and its old UMD build logs a deprecation warning on every page load. The shim imports it as a module (dynamically, so a blocked CDN falls back to the still instead of a blank layer), puts it on `window`, and then loads `script.js` as an ordinary script, so the effect itself stays a plain file you can drop into any build (or none).

**Already using three.js as a module?** Skip the shim and make sure `window.THREE` is set before `script.js` runs:

_Code snippet omitted: it ships with the download._

### Using It With Your Own Design

**What the effect needs from your markup:** a positioned container (`relative`, `absolute` or `fixed`) holding the `[data-silk]` layer and your content. The layer fills the container with `position: absolute; inset: 0`, at any size, so it works on a full-viewport hero or a single card. Your content needs `position: relative` (or any z-index context) to sit above it. The layer takes no pointer events; the container listens for the pointer instead, so the light follows the cursor over your text and buttons too.

**What is only the demo's styling:** everything under `DEMO` in `style.css`: the `.stage` box and the cue line. Replace it wholesale.

**CSS the effect depends on:**

- **The container must have a real size.** The canvas is sized from the layer, which takes the container's box. A container with no height gives a canvas of no height.
- The `.silk` block (`position: absolute; inset: 0`, `overflow: hidden`, `pointer-events: none`), `.silk__canvas`, and the `.gl .silk` rule that drops the still.
- The two still images in `img/`, referenced from `.silk` and its portrait media query.
- On touch screens the container needs `touch-action: pan-y` so vertical swipes still scroll the page. The script sets it inline while it runs if you have not.

**Colours** are custom properties on `.silk`, read once at start and handed to the shader, so a restyle never touches the script:

_Code snippet omitted: it ships with the download._

Any CSS colour works (hex, `rgb()`, `oklch()`, `color-mix()`). Deep, saturated colours look most like satin: emerald, navy, oxblood, aubergine, charcoal. The derived sheen is the base colour taken most of the way to white, and the derived shadow is the base at a fifth of its brightness, which is why a navy or oxblood silk still gets convincing highlights without any other setting. A pale silk (champagne, ivory) works too; its highlight is held back automatically so it does not blow out.

**If you change the colour, re-bake the still.** The still images are a frame of the emerald satin. Screenshot your restyled layer with the content hidden (a landscape frame around 2400 x 1350 and a portrait one around 780 x 1690) and save them over `img/silk-still.webp` and `img/silk-still-portrait.webp`. Until you do, reduced-motion and no-WebGL visitors see the emerald still; the layer's background colour underneath is always your `--silk-color`.

**Contrast.** Behind `data-silk-calm` content the folds flatten, the sheen dims and the cloth is held at or below a fixed brightness (relative luminance 0.13, or the base colour's own if that is brighter), so chalk-white body copy keeps 4.5:1 at the brightest moment. The cap is designed for light text on a deep silk. With a pale silk use dark text and measure again.

### Options

| Attribute | Values | Default | Description |
|-----------|--------|---------|-------------|
| `data-silk` | (present) | | Marks the layer |
| `data-silk-flow` | `0` and up | `1` | Speed of the drifting folds. `0` freezes the folds; the light and ripples still work |
| `data-silk-drift` | `0` | on | `0` stops the light moving on its own. With no pointer it rests high on the left |
| `data-silk-fold` | `0.4` to `3` | `1` | Fold size. `2` gives folds twice as large, `0.6` a finer, busier cloth |
| `data-silk-intensity` | `0` to `1` | `1` | Scales the whole response: how far the light follows the pointer, the ripples and the sheen |
| `data-silk-calm` | (present) | | On content in the same container: quiets the cloth behind it (up to four elements) |
| `data-silk-keys` | (present) | | On a focusable ancestor: enables the keyboard controls below |

With both `data-silk-flow="0"` and `data-silk-drift="0"` the canvas stops drawing as soon as the light settles, and only wakes for input.

### Behaviour

- **Load:** the cloth fades up out of its own shadow while the light swings in from beyond the top left corner, over about 1.6 seconds.
- **Pointer:** the light follows the cursor with weight, a little behind it, raking the sheen across the folds. When the pointer leaves the container the light drifts on its own again (or returns to rest, with drift off).
- **Press and hold:** a ripple lifts from the pointer and rolls outward across the whole sheet, fading as it travels, while the cloth swells gently under the pointer. Holding sends another ripple every 0.85 seconds. Up to six ripples run at once; quick presses stack. Release and the swell settles in 0.9 seconds. Presses on links, buttons and form fields are ignored.
- **Touch:** the light drifts on its own; drag to move it, and it returns to its drift 2.5 seconds after your finger lifts. Tap to send one ripple.
- **Calm zones:** behind marked content the folds flatten and the sheen dims, and the transition into them is soft, so the cloth reads as one sheet.

### Accessibility

- **Reduced motion:** no canvas at all. The layer shows the baked still of the satin, a designed frame at its best light. No autonomous motion of any kind.
- **Without JavaScript, or without WebGL:** the same still. It is never hidden until a canvas actually exists.
- **Keyboard:** with `data-silk-keys` on a focusable ancestor, arrow keys jump the light a step across the cloth (it does not glide), and Space or Enter sends a ripple from the light. The light returns to its drift six seconds after the last key.
- **Screen readers:** the layer and its canvas are `aria-hidden`; nothing in them is content.

### Dependencies

| Dependency | Version | Required |
|---|---|---|
| three.js | 0.180.0 | Yes, as an ES module (see Quick Start) |
| GSAP core | 3.12+ (the demo is built against 3.15.0) | Yes: the load reveal, each ripple's lift, travel and decay, the swell under a held press, the ticker the frame loop runs on, matchMedia branching and teardown |

No GSAP plugins. GSAP plays the timed values; the frame loop owns the continuous ones (the drifting folds and the light's chase of the pointer); the shader turns both into satin each frame. Everything used from three.js (`WebGLRenderer`, `ShaderMaterial`, `PlaneGeometry`, `OrthographicCamera`) is long-stable API, so pinning a different version is a one-line change in the import map.

### Browser Support

Anything with WebGL, which is every current browser. Support is probed before three.js is asked for a renderer: three logs its own failure to the console as errors before it throws, so a visitor with WebGL disabled would otherwise get a console full of red on a page that had quietly fallen back. Probed first, that visitor gets the still and a clean console. In a browser too old for import maps the module never runs, and the still is again what shows.

### Performance

One draw call per frame: a single full-screen pass. The loop runs only while the layer is on screen and the tab is visible. Pixel ratio is capped at 2 (1.5 on touch devices). The satin is smooth, so it holds up well at lower resolution.

To buy back frames on low-end hardware, set `data-silk-flow="0"` and `data-silk-drift="0"`: the canvas then draws only while the light or a ripple is moving, instead of continuously.

## What You Get

- `index.html`: working demo page
- `assets/script.js`: commented, readable source
- `assets/style.css`: effect styles
- `README.md`: full documentation with examples and framework integration notes
- `START-HERE-AI.md`: product-specific copy-paste prompt for AI-assisted setup
- `LICENSE.txt`: standard license terms
- Lifetime updates: re-download anytime from your library

## Get the Code

This is a premium effect. The standard license costs £10 one-time and covers unlimited personal and commercial projects with no attribution required for our code. Bundled third-party assets retain their own licences and attribution requirements. The only restrictions: no redistribution of the code itself and no competing effect libraries.

- [Buy Silk Background](https://gsapvault.com/effects/silk-background)
- [The Vault (£99 one-time, best value): every collection in the Vault library, plus future items added to those collections](https://gsapvault.com/effects)

## Judge the Code Quality First

These related effects are free with complete source published, written to the same production standard (cleanup functions, reduced-motion support, framework-agnostic):

- [Spotlight Background](https://gsapvault.com/effects/spotlight-background.md): A theatrical GSAP and WebGL spotlight background: two stage follow-spots in a hazed room sweep in from the wings, cross over your headline and land on it, then trail the cursor like spots worked by an operator.
- [Parallax Hero](https://gsapvault.com/effects/parallax-hero.md): A pinned hero that pulls its layers into depth from one scrubbed ScrollTrigger: a headline sandwiched inside the stack, ridges or photographs climbing over it, and a sun that sets behind the far layer.

---

From [GSAP Vault](https://gsapvault.com): production-ready GSAP animation effects. Full catalog for agents: https://gsapvault.com/llms-full.txt
