# Lenticular Card

> Tilt a ridged card to reveal an alpine lake from day to night, with optical interlacing, moving plastic highlights and a physical settle.

Canonical: https://gsapvault.com/effects/lenticular-card
Live demo: https://gsapvault.com/demos/lenticular-card/index.html

| Property | Value |
|----------|-------|
| Type | effect |
| Tier | paid |
| Price | £5 |
| Difficulty | advanced |
| Plugins | Core GSAP only |
| Techniques | webgl-shader, 3d-transforms, hover-effect, click-toggle |
| Uses Lenis | No |

## Lighthouse, as measured

Google Lighthouse on the demo, 9 September 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 tactile lenticular-card effect that reveals a second image as the viewing angle changes. The default alpine landscape changes from daylight to a starry night; an alternate guardian-artwork preset is included. A three.js shader interlaces the images beneath a moving plastic highlight, while GSAP plays the view changes and soft release.

## Features

- Two selectable image pairs: alpine lake day/night and dormant/awakened guardian
- Lenticular image switching with clean artwork at either angle extreme
- Ridged optical highlights and restrained perspective tilt
- Pointer movement and horizontal touch drag through the same interaction
- Accessible artwork buttons and instant left/right keyboard selection
- Optional idle sweep with a documented off switch
- Composed fallback for reduced motion, unavailable WebGL or missing dependencies
- Responsive canvas, offscreen pause and complete GPU teardown

## Use Cases

- Travel and destination pages with matched day-to-night landscapes
- Collectible product launches with tactile lenticular-print previews
- Digital trading-card collections with an alternate artwork reveal
- Artist portfolios with paired illustrations in one interactive object

## 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

Two decoded source images become textures on a three.js card. The fragment shader compares viewing angle with each cylindrical lens phase to select the visible artwork and adds a narrow travelling reflection. The render loop smooths pointer input with elapsed-time decay; GSAP tweens the view target for button changes and release easing, while GSAP also handles motion preferences and teardown.

## 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. Copy `assets/` into your project. Keep both images local and serve the page over HTTP or HTTPS.
2. Add the stylesheet and this synchronous fallback probe to your HTML `<head>`. The probe hides the source card only when a WebGL scene can run. Its timeout restores the source if a dependency never arrives.

_Code snippet omitted: it ships with the download._

3. Add the card markup inside your HTML `<body>`. Each source image is decoded once and reused for its GPU texture. Change the image descriptions to match your content.

_Code snippet omitted: it ships with the download._

4. Add the dependencies before your closing `</body>` tag. The classic effect script is appended after the module has loaded; it also runs when the document is already parsed. Load Mona Sans as shown in the demo, or change the two font declarations in `makeTexture()` alongside your own CSS.

_Code snippet omitted: it ships with the download._

Already using three.js? Assign your imported namespace to `window.THREE` and load the effect once. Keep one three.js version in the page rather than adding another copy. `script.min.js` can replace `script.js` in production.

### Using It With Your Own Design

The contract is one `[data-lenticular]` root, one `[data-lenticular-source]` figure, and exactly two `[data-lenticular-image]` images in viewing order. Optional `[data-lenticular-show="0"]` and `[data-lenticular-show="1"]` buttons must be inside the root. The script queries these data attributes, so the decorative class names may change with your CSS.

Give the root a real width and height, `position: relative`, and `touch-action: pan-y`. The inserted canvas fills it absolutely and ignores pointer events. Place your controls above it with `position: relative` or `absolute` and `z-index: 2`. Keep the source card fully composed before JavaScript, then hide it only under `html.gl` or the root's `.is-live`. The opacity gate is essential: a child whose `visibility` is restored for alternate artwork must not appear through a hidden source figure. Never hide the fallback under `.has-js`.

The grey page, shadow oval, toolbar, prompt and spacing are demo CSS and can be replaced. The framed card graphic is assembled in `makeTexture()`: this small function draws your image, the dark caption scrim, border, title, subtitle and orange mark onto a local texture. Replace that composition there for a different card design; update the fallback figure to match. Keep the title metadata and figure caption aligned. No effect logic depends on the landscape, guardian artwork or captions. The optional `assets/demo.js` sets the chosen demo images and labels before the renderer initializes; omit it and the preset picker when supplying your own pair.

### Demo Presets

The demo opens with **Landscape**: Alpine Lake, with Day and Night view buttons. Choose **Guardian** in the labelled Artwork selector to reuse the same card with the preserved Kogen illustrations and Dormant/Awakened buttons. `?artwork=guardian` opens that preset directly; an absent or unknown `artwork` value opens Landscape. Changing the selector reloads the same stage with that query value, preserving other URL parameters. The thumbnail always uses Landscape.

The selector is optional demo code in `assets/demo.js`, loaded after the source markup and before the dependency/module shim. Its `[data-lenticular-preset]` select and `[data-lenticular-preset-control]` label choose the preset; `[data-lenticular-caption]`, `[data-lenticular-subcaption]` and `[data-lenticular-controls]` identify the demo caption and control-group text it updates. The core renderer queries none of these demo hooks. Without JavaScript the default daylight card is visible and the inactive preset picker stays hidden.

### Options

Attributes go on the `[data-lenticular]` root. Numeric zero values are respected.

| Attribute | Values | Default | Description |
|---|---|---|---|
| `data-auto` | `0`, `1` | `1` | Sweeps the viewing angle after 3.5 seconds without input. Set to `0` for a still stage or deterministic captures. This option is also read while running. |
| `data-ridges` | `40`–`300` | `160` | Number of cylindrical lens bands across the card. Higher values are finer. |
| `data-light` | `0`–`2` | `1` | Strength of the travelling plastic highlight; `0` disables it. |
| `data-title` | Text | `Kogen` | Title painted on both card textures. |
| `data-subtitle` | Text | `The ember guardian` | Small caption painted on both textures. |

The demo explicitly supplies `Alpine Lake` and `From daylight to starlight`; the renderer's unconfigured fallback captions remain the guardian defaults above. Set texture-related options before initialization. Update captions through a fresh instance after changing them; they are painted once, not every frame.

### Choosing Your Images

Use two portrait images at a **2:3 aspect ratio**, ideally 1024×1536 or greater. The included frame draws the whole image into the same fixed portrait rectangle, so crop other aspect ratios beforehand to prevent stretching. Align the mountain ridgeline, horizon and reflection, or a character's eyes, face and silhouette. The effect should reveal a clear change in lighting, costume or state without a large jump in subject position.

The transition runs horizontally through fine vertical strips. Large shapes and clear tonal differences survive that interlacing better than tiny unrelated details. The same alpine lake in daylight and under stars gives a strong, readable light change. The retained pale dormant and orange awakened guardian pair demonstrates the same mechanism with illustration. No colour grading is required. Keep images same-origin, or configure image CORS correctly before drawing them into a texture.

The included alpine scenes and guardian illustrations are original generated assets. Their provenance and prompts are in `assets/img-src/PROMPTS.md`. The JPEG files are masters; the WebPs in `assets/img/` are the browser assets.

### Accessibility

Pointer movement and horizontal touch drag share the same viewing input. The card captures an active pointer and releases it on pointerup or cancellation; vertical touch gestures remain available to the page. Left/right arrow keys select the current pair immediately when the card or its view buttons have focus. The native Artwork select keeps its normal keyboard behavior; pointer interaction with the select does not tilt or drag the card. Focus on the stage demonstrates the alternate view, and the real buttons expose selection through `aria-pressed`.

Reduced motion creates no WebGL canvas and runs no idle sweep. The composed source card remains visible, and buttons or arrow keys switch the static images. No JavaScript or unavailable WebGL also leaves the source card visible; without JavaScript the first image is the static presentation.

Device orientation was deliberately omitted: horizontal touch drag already maps precisely to the optical viewing angle without permission or iframe sensor policies. Motion sensors are not required for any part of the interaction.

### Browser Support

The animated surface requires WebGL 2, ES modules, import maps and modern pointer events. Current browsers supporting these APIs run the effect; restricted graphics contexts and failed library loads retain the static card. A network error may still appear in a browser's developer console, while the fallback remains usable. Test from a local server rather than opening the HTML as a `file:` URL.

### Performance

Pixel ratio is capped at 2 on fine pointers and 1.5 on coarse pointers. The shader samples two textures and shades the lens bands in a single pass; no post-processing pipeline is allocated. ResizeObserver tracks the root rather than the window, and rendering pauses offscreen or in a hidden tab. Lower the pixel-ratio caps in `initialize()` first if a slower device needs less work. Texture size is the next lever.

The continuous pointer chase uses elapsed-time decay and clamps long frames. GSAP tweens only the view target for click changes and the 600ms release; the render loop reads that target and owns the optical surface and 3D pose.

### Dependencies

| Dependency | Role |
|---|---|
| GSAP 3.12+ (demo uses 3.15.0) | Artwork-change and release target tweens, `gsap.context()` and `gsap.matchMedia()` lifecycle. No plugins. |
| three.js r180 | Renderer, camera, card meshes, textures and custom optical shader. |
| Mona Sans | Demo typography and the caption painted onto the textures; a system font fallback is supported. |

## 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 £5 one-time and covers unlimited personal and commercial projects with no attribution required. The only restrictions: no redistribution of the code itself and no competing effect libraries.

- [Buy Lenticular Card](https://gsapvault.com/effects/lenticular-card)
- [The Vault (£99 one-time, best value): everything in the catalogue, including future releases](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):

- [Hover Underline](https://gsapvault.com/effects/hover-underline.md): Four material link underlines (an exit-through line, marker sweep, hand-drawn wave, and an endlessly travelling wave) with coordinated type and active-index responses.
- [3D Card Flip Gallery](https://gsapvault.com/effects/3d-card-flip.md): Tactile GSAP 3D cards with deep perspective, reactive edge lighting and shifting shadows. Flip on hover, keyboard focus or tap, with grouped auto-close.
- [Image Clip Reveal](https://gsapvault.com/effects/image-clip-reveal.md): A cinematic image reveal where a directional polygon aperture opens as the photograph settles from a restrained Ken Burns scale and its caption lands.

---

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