# Lenticular — four cards that change as you tilt them

A drop-in piece from [Funeral Thirsty](https://www.funeralthirsty.com/studies/lenticular.html). Give it a list and a folder of frames. It builds four cards. Each card is one photograph and a second frame of the same picture, shown through ridges drawn live on a canvas. Tilt the phone, or move across a card, and the photograph becomes the other frame.

The two frames have to register. On the study, the second frame is the same picture rendered again — diamond painting, halftone, cross-stitch, or type — so the ridges line up.

No build step, no dependencies, no framework. One script, one attribute.

## Unzipping

The zip is flat: its files land in whatever folder you unzip it into, with `index.html` at the top. Make a folder called `lenticular` first if you want them contained. Design tools that import the zip open that `index.html`, the site, as the entry.

## The minimum that installs it

```html
<main id="cards" data-dir="assets/sample/"></main>
<script src="lenticular.js"></script>
```

Size the cards with CSS. Each print is a 3:2 frame; the canvas fills it, inset by the mat. Without a width and an aspect ratio the canvas measures 0 and the cards never become ready. The styles are in `lenticular.css`, and in `styles.css` as the site uses them.

Serve the page over a web server. Browsers refuse to let a page read a photograph's pixels when it is opened straight from disk. Any server will do, including `python3 -m http.server`. The frames must be on the same origin as the page, or served with a permissive `Access-Control-Allow-Origin` header.

`data-dir` is the folder of frames, with or without a trailing slash. Leave it off and the engine loads `../assets/studies/lenticular/`, which is the study's own folder and will 404 inside this kit. A kit page always sets the attribute. The path is resolved against the page, not against the script.

`#how` and `button#tilt` are optional. Leave them out and the cards still run. The button only exists for the iOS permission prompt; everywhere else the tilt listener is attached on load, and a pointer still changes the angle.

## What is in this folder

| Path | What it is |
|---|---|
| `index.html` | The site: the Lenticular study page itself, rebuilt from the files in this kit. Open this first. Uses `styles.css` and `lenticular.js`. |
| `lenticular.js` | The engine. Builds the four cards, draws the ridges, and exposes `window.Lenticular`. |
| `lenticular.css` | The cards, and the few styles the demo and previews use. |
| `demo/index.html` | The four cards, and nothing else. No hint line, no tilt button. |
| `OPTIONS.md` | The DOM the engine expects, the one attribute it reads, and the JavaScript interface. |
| `components/` | Preview pages for design tools: the cards, the palette, the type. Each opens in a browser on its own. |
| `tokens/tokens.json` | The kit's colours, type and sizes in the W3C Design Tokens format (2025.10). |
| `tokens/tokens.css` | The same values as CSS custom properties. |
| `DESIGN.md` | The guideline: colour, type, spacing, layout, components, motion, voice, brand, anti-patterns. |
| `design-system-spec.json` | The manifest Claude Design reads. |
| `manifest.json`, `tokens.css`, `design-tokens.json`, `components.html`, `USAGE.md`, `preview/` | The Open Design layer: its manifest, its token contract, its token JSON, a component fixture, a usage guide and preview pages (the site, colors, typography, spacing). |
| `assets/sample/` | Eight sample photographs, generated for the studio and released under CC0. Four pictures, each with a second frame. |
| `assets/fonts/` | IBM Plex Mono, under the SIL Open Font License, with the license text. |
| `LICENSE.md` | MIT for the code, CC0 for the photographs, and what is not licensed. |

## Loading it into a design tool

The kit carries two layers, one for each tool, describing the same system.

**Open Design.** Import `lenticular.zip` and it opens on `index.html`, the site, with every other page listed alongside it. Read as a design system, the same archive supplies `manifest.json`, `DESIGN.md`, `tokens.css`, `components.html`, `USAGE.md` and the pages under `preview/`, and Lenticular becomes a system other work can be rendered in.

**Claude Design.** Reads `design-system-spec.json`, the component previews under `components/` with their first-line `@dsCard` markers, and `tokens/tokens.json` in the W3C Design Tokens format.

## Making it yours

Change `--felt`, `--felt-deep`, `--ink` and `--ink-soft` in `tokens/tokens.css` (or the matching names in `tokens.css`) and the page around the cards moves with them. `--card` is the paper of the print. `--red` is the study's eyebrow and the focus ring. The engine does not read those colours. The ridges take their colour from the photographs.

`--pitch` is the one token the engine reads, and it reads it live, on every resize. It is 5px, and 4px below 860px. Change it and the ridges change width.

The rest angle (0.12), the distance the boundary travels across one ridge (0.11), the card's tilt (18°) and the phone tilt that runs the whole flip (20° either way) live in `lenticular.js`. The token file records those numbers. Editing the token does not change them; edit the script.

The four cards and their frame filenames live in the `CARDS` list in `lenticular.js`. Replacing a photograph means a file of the same name in the `data-dir` folder, or a new name in that list. The two frames of a card should be the same picture, or the ridges will not register.
