# 歪む画像 — Style Reference
> 炭色の面に一枚の舞台写真。カーソルのまわりだけ空気のように波打ち、縁に赤と青の色ずれが出る。文字は白の太字と珊瑚のラベル。

**Theme:** dark  **Series:** PARTS 058  **World:** 小さな劇団。来月の公演、稽古場の一枚。

## Concept
画像を置く区画のパーツ。主役は WebGL で描く画像で、UV に「時間で流れるノイズ」と「カーソルから同心円に広がる波」を足して歪ませ、変位の向きに R と B をずらして色収差を出す。
歪みはカーソルからの距離で減衰し、画像の外では止まる。無操作が続くと、カーソルは写真の人物のまわりを自動で巡り、静止画でも波が写る。画像は 1 枚だけ、base64 で埋め込み(`file://` でもテクスチャが汚染されない)。

## Signature Technique
WebGL1、画像テクスチャを貼った全画面(枠内)三角形。外部ライブラリなし。

- 枠: `.frame`(比率 4:3、最大 820px)。canvas は枠の大きさ × SCALE(desktop 1.5 上限 / mobile 1)。
- cover: 画像比と枠比から `uCover` を求め、`cuv = (uv - .5)·cover + .5` で切り抜く。
- 減衰: `d = (uv - mouse)·(1.333, 1)`、`fall = exp(-|d|² / RADIUS²)`(RADIUS .55)。
- 波: `ring = sin(|d|·30 - 3t)·.5`(カーソルから同心円)、`n = (noise(5uv + (.6t,0)) - .5, noise(5uv + (3.7,.5t)) - .5)`(時間で流れる 2D value noise)。
- 変位: `disp = (1.4n + normalize(d)·ring)·AMP·fall·hover`、AMP .035。
- 色収差: `off = disp·SPLIT`(SPLIT 1.2)、R は `+off/2`、B は `-off/2`、G は中心。変位の向きに沿ってずれる。
- 仕上げ: 周辺減光 25%、1/180 のディザ。
- hover: 枠の中で 1、枠の外で 0(自動のときは .8)。lerp .05 でゆっくり立ち上がる。カーソル座標は lerp .08。
- 自動: `idle > 2s` で `mouse = (.5 + .22 sin .45t, .55 + .2 sin(.77t + .8))`(人物のまわり)。起動直後から自動。
- 画像: `<img>` を base64 の data URI で埋め込み、`load` 後に `texImage2D`。失敗時は `.frame.still` で `<img>` をそのまま表示。
- WebGL 非対応: 同じく `.still` で静止画。

## Color Palette
| Token | HEX | 役割 |
|---|---|---|
| --carbon | #0A0A0A | 地 |
| frame-bg | #111111 | 画像の読み込み前の枠 |
| --bone | #CACACA | 本文・キャプション |
| white | #FFFFFF | 見出し・ロゴ・リンク |
| --iron | #727272 | ラベル・フッター |
| --coral | #FF7777 | eyebrow とフォーカスリングだけ |
| image | マゼンタの照明と黒 | 写真そのものの色。UI には使わない |

## Typography
- 単一ファミリー: Noto Sans JP。
- 見出し: 700、`clamp(34px, 4.6vw, 66px)`、line-height 1.05、letter-spacing -.03em、白、`<br>` で2行固定。
- 本文: 400、`clamp(14px, 1.1vw, 17px)`、line-height 1.9、最大 28em、1文。
- ラベル: 12px(mobile 11px)、letter-spacing .14〜.22em、大文字。eyebrow は珊瑚。
- キャプション(.cap): 11px、letter-spacing .12em、大文字、`text-shadow` 付き、写真の下端に左右2つ(padding 12px 14px)。
- リンク(.link): 14px、letter-spacing .06em、下線 1px rgba(202,202,202,.4)、下 6px、右に間隔 12px で 18px の横線。

## Spacing & Layout
- 1画面 = `100svh`、`grid-template-rows: auto 1fr auto`。中段は 5:7 の 2 カラム(本文 / 写真)、gap `clamp(24px, 5vw, 80px)`。写真は右寄せ。
- 本文は最大幅 520px。eyebrow の下 22px、見出しの下 22px、本文の下 30px。左右余白 `clamp(20px, 5vw, 72px)`、上 28px、下 22px(+safe-area)。
- breakpoint 860px: 1 カラム(gap 26px)で写真を上に、幅いっぱい。640px: 見出し `clamp(28px, 7.8vw, 38px)`、フッター2行積み。

## Motion
- 波の時間係数 SPEED 1.4。同心円は 3 rad/s で外へ流れる。
- hover の立ち上がり lerp .05(約 1 秒)、カーソル追従 lerp .08。
- 自動は 14 秒前後で一巡。操作すると即止まり、2 秒後に再開。
- requestAnimationFrame 1本。`visibilitychange` と IntersectionObserver で画面外は停止。
- `prefers-reduced-motion`: mouse (.55,.5)、hover .8 の静止フレームを1回(画像 load 後にもう1回)描く。

## Components
- hero: 全画面。上に劇団名と分類、左に eyebrow / h1 / lead / リンク、右に写真。
- frame: 4:3 の枠、角丸なし、下端に等幅風のキャプション 2 つ。
- link(.link): 下線付きのテキストリンク+横線。hover で下線が白に。focus-visible は珊瑚のリング。
- footer(.bot): 11px、左に操作説明、右に `VALE / PARTS 058 / 歪む画像`。

## Do / Don't
- Do: 歪みはカーソルのまわりだけ。全面を歪ませると画像が壊れて見える。
- Do: 色収差は変位の向きに沿わせる。固定方向だと印刷ずれに見える。
- Do: 歪みは AMP .035 まで。それ以上は写真が溶けて何の絵か分からなくなる。
- Don't: 写真を 2 枚以上置かない。
- Don't: UI に写真の色(マゼンタ)を使わない。珊瑚 1 点だけ。
- Don't: 角丸や影を付けない。写真は壁に貼った印画紙のまま。

## References
- Channel Studio: 近黒の面に写真を壁のように置き、文字は 1 書体 1 ウェイト、珊瑚 1 色という構成を抽象化。
- MasterClass: 黒い舞台にマゼンタの照明という写真の内容と、見えない面(影や枠なし)の考え方を抽象化。マゼンタは写真の中にだけ置いた。

## Usage
1. `.frame`(canvas + img + cap)と `<script>` ブロック、`.frame` 系の CSS を切り出す。
2. 画像は `<img id="src">` の `src` を差し替える。別オリジンや `file://` の画像は WebGL で汚染されるため、同一オリジンに置くか data URI にする。
3. パラメータは JS 冒頭の `P`:
   - `SCALE` 描画解像度倍率
   - `AMP` 歪みの大きさ(UV 比)、`FREQ` ノイズの細かさ、`SPEED` 波の速さ
   - `RADIUS` カーソルの影響半径(高さ比)
   - `SPLIT` 色ずれの強さ(0 で無し)
   - `AUTO_IDLE` 自動で波が走るまでの秒数(大きな値で自動なし)
4. 同心円の密度は FS の `dist*30.`、ノイズの重みは `n*1.4`。
5. 枠の比率は CSS の `aspect-ratio`。`cover()` が自動で切り抜く。
6. 複数の枠に使うときは枠ごとに WebGL コンテキストを持つ(上限 16)。多いなら 1 枚の canvas を画面全体に敷き、`scissor` で枠ごとに描く。
