# 溶ける文字 — Style Reference
> 白い磁器の作業台に、太い墨の一字。下の縁から列ごとに違う速さで垂れ落ちる

**Theme:** light  **Series:** PARTS 019  **World:** 架空の墨の工房「Sumi」のトップ。濃い墨ほどゆっくり垂れる、という素材の性質を一字で見せる。

## Concept
画面の右に「墨」の一字を置く。字のラスタを毎フレーム重力方向へ押し流し、列ごとに違う減衰を与えるので、ほとんどの列はわずかに垂れるだけ、ごく一部の列だけが長い滴になる。字そのものは動かず、滴だけが伸びるので読めたまま溶ける。10 秒で紙に戻り、また新しい字が置かれる。カーソルを当てた所は熱で速く長く溶ける。色は紙と墨と、品番の翡翠色だけ。

## Signature Technique
WebGL1 の ping-pong。字のマスクは別テクスチャで固定し、滴の量だけを時間発展させる。

- **初期化**: 2D canvas に Noto Sans JP 900 で一字を白で描く。字の大きさは `min(H·0.62, W·0.5)`(モバイルは `min(W·0.78, W·0.5)` で、実質は幅の 50%)。これを`glyph` テクスチャと状態テクスチャの初期値にする。解像度は画面と同じ(dpr 1)。
- **列の性質**: `a = 0.6·noise(x·0.05) + 0.4·noise(x·0.185)`、`drip = smoothstep(0.56, 0.94, a)`。`loss = mix(0.22, 0.0011, drip)`(減衰。長さは約 ln2/loss texel: 3px〜630px)、`sp = 0.5 + 1.0·noise(x·0.065)` texel/フレーム(速さ)。
- **熱**: `heat = 1 + 3·exp(−|p − cursor|² / 110²)`。速さに掛け、減衰を割る。
- **更新**: `new = max(glyph, max(here·(1 − 0.15·loss), above·(1 − loss)))`。`above` は `sp` texel 上をバイリニアで読む。滴の先端は毎フレーム sp だけ下がり、`(1 − loss)` 倍ずつ薄くなって 0.5 を切ると消える。
- **描画**: `ink = smoothstep(0.46, 0.56, a)` で紙 `vec3(0.969) − 0.01·|uv − 0.5|`(#f7f7f7 から端へわずかに暗くなる)と墨 `vec3(.09, .086, .094)`(#171618)を混ぜる。しきい値のすぐ内側に 8% の明るさを足して濡れた縁にする。
- **周期**: 10 秒。終わりの 0.6 秒で墨をフェードし、`reset()` で字を置き直す。読み込み時は 1.4 秒ぶん(84 ステップ)進めてから表示する。更新は1フレーム1ステップで、dt を掛けない(リフレッシュレートが高い画面では速く垂れる)。周期の時計だけ dt(上限 0.05 秒)で進める。

```glsl
float drip=smoothstep(.56,.94,a); float loss=mix(uLossMax,uLossMin,drip);
float sp=uSpeed*(.5+1.0*n1(x*uFreq*1.3+3.))*heat; loss/=heat;
float g=texture2D(uG,v).r; float above=texture2D(uS,v+vec2(0.,sp/uRes.y)).r*(1.-loss);
gl_FragColor=vec4(max(g,max(here*(1.-loss*.15),above)),0.,0.,1.);
```

## Color Palette
| Token | HEX | Role |
|---|---|---|
| --canvas | #f7f7f7 | 紙。シェーダーの地も同じ値 |
| --ink | #171618 | 墨、見出し、クレジット |
| --graphite | #555555 | 本文 |
| --pewter | #9a9a9a | ヘッダー補助とメタ情報と操作説明 |
| --jewel | #0e7a5c | 墨の品番だけに使う翡翠色 |
| --mist | #e6e6e6 | 予備の罫線色 |

## Typography
- 墨の字: Noto Sans JP 900、canvas に描く。1 字。
- 日本語見出し: Noto Sans JP 400、`clamp(18px, 1.6vw, 22px)`、line-height 1.6。本文 300、`clamp(13px, 1.05vw, 15px)`、最大幅 26em。本文は2文を `<br>` で分け、語の途中で折り返さない。
- ラベル: JetBrains Mono 400、11–12px、letter-spacing 0.06em。
- WebGL 不可時は `.fallback` に同じ字を HTML で表示。

## Spacing & Layout
- hero `min-height: 100svh`、3行グリッド。canvas は背面全面。
- 字の中心は (68%, 42%)(モバイル (50%, 36%))。見出しと本文は左下。
- 余白: 横 `clamp(20px, 5vw, 72px)`、縦 `clamp(20px, 3vw, 40px)`。モバイル左右 18px。
- breakpoint 760px: ステージを1列にし、ヘッダー右とメタ情報を消し、フッターを縦積み。

## Motion
- 滴は 0.5〜1.5 texel/フレーム(60fps で毎秒 30〜90px)で伸び、列ごとの長さで止まる。長い滴は 10 秒で画面下まで届く。
- 周期 10 秒(0.6 秒のフェード付き)。
- rAF 1本。IntersectionObserver と visibilitychange で画面外停止。
- `prefers-reduced-motion: reduce`: 1.4 秒ぶん進めた状態で止め、周期も回さない。

## Components
- hero: 背面 canvas(`aria-hidden`)+前面テキスト。
- header: 左に工房名、右に技法名。
- copy: 見出し1行+本文2文(2行)、左下。
- meta: 左下に墨の品番(翡翠色)と周期。
- footer tag: `VALE / PARTS 019 / 溶ける文字` と操作説明1行(タッチ端末では差し替え)。

## Do / Don't
- Do: 字は1字か短い1語。画数の多い字ほど水平の縁が多く、滴が出る場所が増える。
- Do: 字のマスクは固定し、滴だけ動かす。字まで流すと読めなくなる。
- Do: 滴になる列は 10% 以下に絞る。
- Don't: 墨に色やグラデーションを付けない。
- Don't: 滴を左右に揺らさない。重力は一方向。
- Don't: 周期を 6 秒より短くしない。

## References
- Cycle: 白い磁器のような作業台に墨のインク、宝石色の小さなラベルという配色を借りた。ラベルは品番1つだけにした。
- Dyson: 暖かい紙に製品を標本のように1点置く構成を借りた。製品の代わりに字を1字置き、素材の振る舞いを見せた。

## Usage
1. `<canvas id="gl">` を hero の背面に置き、`<script>` ブロック全体を移植する。
2. `P.word` と `drawWord()` の書体・大きさ(`fs`)・中心(`cx, cy`)を変える。複数文字でも動く。`.fallback` の字も揃える。
3. `P.lossMin / lossMax / dripFreq` で滴の長さと本数、`P.speed` で速さ、`P.cycle` で周期を決める。
4. 色は `FS_DRAW` の `paper` と墨の色、CSS の `--canvas / --ink` を揃える。
5. 周期の表示 `#cyc` と操作説明 `#hint` は JS が書き込むので、要素を消すときは該当行も消す。

| パラメータ | 既定 | 意味 |
|---|---|---|
| cycle | 10 | 字を置き直す周期(秒) |
| speed | 1.0 | 滴の基準速度(texel/フレーム) |
| lossMin / lossMax | 0.0011 / 0.22 | 滴の列と、それ以外の列の減衰 |
| dripFreq | 0.05 | 列のばらつきの周波数(1/px) |
| heatR / heat | 110 / 3.0 | カーソルの熱の半径(px)と倍率 |
| start | 1.4 | 読み込み時に進めておく秒数 |


