# Schichtplan Nano promo — WebGL liquid-metal background

**Last Updated:** 2026-04-02

## Purpose

The `/schichtplan` Nano AI block (`#schichtplan-neu-nano-promo`) uses a **vanilla WebGL2** fullscreen-quad fragment shader for a **viscous liquid metal / thick oil** look: domain-warped FBM, dual specular lobes, Fresnel rim, palette **near-black → charcoal / slate → muted silver-blue crests** (reference tone: satin, not mirror chrome). **Scroll** drives `u_scroll` / `u_scroll_smooth` (section center vs viewport) for parallax-like phase and lighting shift. Conceptually related to common “liquid metal hero” articles such as [Ace Studio’s Medium walkthrough](https://medium.com/@ace_studio/how-to-add-a-liquid-metal-webgl-effect-as-a-background-8fcc42730783) and [Flowfav’s cloneable](https://www.flowfav.com/cloneable/liquid-metal-webgl-background-webflow-cloneable); **Ordio-original GLSL** only.

## Files

| Piece | Path |
|-------|------|
| Markup (canvas + CSS fallback blobs) | [`v2/sections/partials/product-schichtplan-neu-nano-promo.php`](../../v2/sections/partials/product-schichtplan-neu-nano-promo.php) |
| WebGL bootstrap | [`v2/js/schichtplan-nano-liquid-metal.js`](../../v2/js/schichtplan-nano-liquid-metal.js) (minified: `schichtplan-nano-liquid-metal.min.js`) |
| Stacking, `gl-active`, blob fallback | [`v2/css/product-pages.css`](../../v2/css/product-pages.css) |
| Page script include | [`v2/pages/product_schichtplan_neu.php`](../../v2/pages/product_schichtplan_neu.php) |
| Minify registry | [`minify-assets.js`](../../../minify-assets.js) |

After editing JS/CSS: `npm run minify`.

## Fallback rules

1. **`prefers-reduced-motion: reduce`** — script does not initialize WebGL; animated CSS blobs stay (animation off via existing CSS).
2. **No WebGL2** or **shader link/compile failure** — fail-silent (no `console.*`); CSS blobs remain visible; section never gets `schichtplan-neu-nano-promo--gl-active`.
3. **`webglcontextlost`** — animation stops, GPU handles released without `delete*` on a lost context; `gl-active` removed; CSS fallback visible. **`webglcontextrestored`** — pipeline rebuilt and observers reattached.

## Performance checklist

- Canvas backing store uses **DPR cap** by width and **`pointer: coarse`** (lower caps on phones for GPU/thermal headroom).
- **ResizeObserver** on the section; **IntersectionObserver** + **scroll** (`passive`) + **Page Visibility** restart or stop the RAF loop when the section nears or leaves the viewport; **`u_time` is not reset** on every IO callback (only when (re)starting a stopped loop) so motion does not freeze.
- **`powerPreference: 'default'`** on context.
- With **`schichtplan-neu-nano-promo--gl-active`**, CSS blobs are hidden to avoid stacked blurs + WebGL.

## Tuning (shader + uniforms)

Adjust in `v2/js/schichtplan-nano-liquid-metal.js`:

- **Motion speed:** `tm = t * 0.4`, `flow` drift inside `heightField`, orbit `t * 0.42`.
- **Scroll strength:** `scr * …` in `heightField`, `orbit += scr * 1.65`, and JS `scrollSmoothed` blend factor (`0.16`).
- **Palette:** `abyss` / `charcoal` / `slate` / `steel` / `crest`; spec exponents `40` / `28` (satin).
- **Scale / warp:** `st * 2.65`, warp `0.38`, `fbm4`.

## Optional smoke test

Requires dev server and Playwright (`npm i` devDependencies). The script launches Chromium with **`--enable-unsafe-swiftshader`** so **headless** runs still obtain a WebGL2 context on typical CI/macOS environments.

```bash
node v2/scripts/dev-helpers/smoke-schichtplan-nano-webgl.mjs http://127.0.0.1:8003
```

Exits `0` if the promo canvas exists and `getContext('webgl2')` succeeds in Chromium; `1` otherwise (e.g. server down).

## Related

- Inventory pointer: [`docs/content/pages/product-pages/PRODUCT_PAGES_INVENTORY.md`](../../content/pages/product-pages/PRODUCT_PAGES_INVENTORY.md)
- Product page rule: [`.cursor/rules/product-pages.mdc`](../../../.cursor/rules/product-pages.mdc) (WebGL section backgrounds)
