Quick Start
1. Add to your HTML <head>:
Code snippet omitted: it ships with the download.
2. Add the surface to your <body>:
Code snippet omitted: it ships with the download.
3. Add before the closing </body> tag:
Code snippet omitted: it ships with the download.
Already using three.js in a bundled application? Use your existing compatible import, expose it as window.THREE, then load script.js after GSAP. Do not load a second three.js copy or the retired UMD build. The script handles both immediate and deferred execution.
Using It With Your Own Design
Markup contract: each [data-prismatic-glow] is one independent surface. Keep .prismatic-glow on it and include the decorative .prismatic-glow__fallback. Give the root a real height through your layout. Ordinary HTML sits above the border. An optional [data-glow-trigger] button provides keyboard activation. Any non-control area of the surface can also be clicked or tapped. Existing links, form inputs and unrelated buttons keep their own actions.
Demo-only styling: delete the .showcase-*, .glow-controls and .glow-control rules and the corresponding demo content. Inter, the edge-to-edge layout and the corner icon controls are presentation choices. The multicolour border and its pale centre deliberately echo soft illuminated product UI; the demo also includes dark appearance.
Required CSS: retain the root's relative positioning, isolation, clipped overflow and rounded corners; retain the absolute canvas/fallback placement, negative stacking layer and pointer-events:none. Keep touch-action:pan-y so the surface does not prevent normal page scrolling. The static fallback remains painted until the canvas has rendered, then .is-live hides it. No semantic content is hidden while dependencies load. The shader reads the root's top-left CSS radius as its uniform corner radius; use one pixel radius for all four corners.
For dark surfaces use data-glow-theme="dark". The shader's centre is neutral #f4f4f4 in light mode and #111111 in dark mode. Match your surrounding surface to these colours or change the two background values in the fragment shader together with --surface in the CSS. This version fills its entire root; it is not a transparent overlay over photography.
Options
Set options before initialization; the script reads them once per instance.
| Attribute | Values | Default | Description |
|---|---|---|---|
data-prismatic-glow |
Present | Required | Initializes a glow surface |
data-glow-width |
1–120 |
28 |
Resting inward glow width in CSS pixels |
data-glow-strength |
0–2 |
1 |
Colour intensity; 0 leaves a plain centre colour |
data-glow-ambient |
0–4 |
1 |
Slow colour drift multiplier; 0 disables it |
data-glow-pointer |
0, 1 |
1 |
Respond to mouse or pen movement near the border |
data-glow-duration |
0.3–5 |
1.5 |
Full pulse duration in seconds; most energy settles within about 0.9 seconds |
data-glow-pixel-ratio |
0.5–2 |
2 fine / 1.5 coarse |
Rendering resolution cap, also limited by device pixel ratio |
data-glow-theme |
light, dark |
light |
Surface appearance |
data-glow-trigger |
Present on a button | Optional | Triggers the same ripple with click, Enter or Space |
data-glow-theme-toggle |
Present on a button | Optional | Toggles dark appearance; script updates aria-pressed |
Accessibility
The canvas and fallback are decorative and hidden from assistive technology. Keep your content in semantic HTML and provide a real [data-glow-trigger] button for keyboard use. Each icon activation chooses a new point around the border, at least a quarter turn from the previous one. Enter and Space use the same behaviour as a click. The demo provides visible focus outlines and labelled ripple and appearance icons in its top-right corner.
With prefers-reduced-motion: reduce, the scene and its ticker are not created. The static CSS border and your HTML remain visible. The demo hides its unavailable animation controls. A live change to motion preference disposes or reconstructs the scene through GSAP matchMedia. No JavaScript and no WebGL use the same static treatment. The CSS fallback uses its own fixed colour width; numeric shader options affect the animated version only.
The ripple is decorative feedback: never rely on it alone to communicate success or another essential state. Use your normal text or status announcement for that.
Dependencies
- GSAP 3.15.0 core: pulse envelope and easing, shared ticker, matchMedia and context cleanup. No plugins or Lenis.
- three.js r180: renderer, full-screen plane and fragment shader. The shader owns spatial wave travel, colour separation and glow falloff.
- Inter: loaded only by the demonstration; any UI font works.
Browser Support
The animated version requires WebGL 2, JavaScript modules/import maps, ResizeObserver and IntersectionObserver, available in current evergreen browsers. Browsers or devices without usable WebGL receive the CSS border. A lost GPU context temporarily restores that fallback and resumes rendering when the context returns. The static version requires CSS conic gradients and supports the same modern browser family.
Performance
One plane, one draw call, no textures, image requests or postprocessing buffers. Rendering stops while the surface is outside the viewport or the document is hidden. With ambient drift set to 0, it also stops after a pulse settles. Pixel ratio is capped at 2 for fine pointers and 1.5 for touch.
The first lever on slower devices is data-glow-pixel-ratio="1"; the soft glow tolerates a lower resolution well. Keep the number of simultaneous full-screen glow surfaces small because each instance has its own WebGL context. Prefer data-glow-ambient="0" for an interaction-only effect.
Worked examples, the events and programmatic API, and the class reference ship with the download, alongside the full source.