# DOX — packages/deck3d/src/fx

Curated, licence-clean effect corpus + deterministic defaults + composition (design D9). See change: add-deck3d-presentation-package.

| File | Purpose |
|------|---------|
| `AGENTS.md` | This file. |
| `types.ts` | `FxKind`/`FxMode`/`FxCard`/`FxContext`/`FxHandle`/`FxFactory`/`FxEntry` + `PERMISSIVE_LICENCES` + `FX_TOPICS`/`FxTopic` (closed 12-value topic vocabulary). Every effect module implements `create(ctx, params) → { object?, pass?, material?, tick?, dispose() }`. `FxContext` carries `rng` (seeded per slide) + read-only `slide`. See change: deck3d-cinematic-worlds. |
| `meta.schema.json` | Card JSON Schema (draft-07): id, kind, tags (`mood`/`content` free-form + optional closed `topic[]`), cost 1–5, modes, params schema, conflicts, `https://` source OR the literal `local`, SPDX licence enum. See change: deck3d-cinematic-worlds. |
| `index.ts` | GENERATED registry: pairs each `<id>.meta.json` card with its `<id>.ts` factory; `FX_IDS`, `cardFor`. |
| `<id>.ts` + `<id>.meta.json` | One module + card per effect (80 ids, see `reference/effects.md`). Stylistic backgrounds (Section 18, three.js example ports, NO `tags.topic` so routing is untouched): `clipped-solids` (`mode` planes|intersection|caps — one centred knot; three discards a plane's NEGATIVE side so normals lean -z to carve the camera-facing octant; caps are clipped by the NEGATED other planes; a stencil-capable composer target is required, see `runtime/post.ts`), `extruded-shapes` (built-in parametric paths + `svgPath` = SVG `d` string via `SVGLoader`, bad data falls back), `scatter` (`MeshSurfaceSampler.setRandomGenerator(ctx.rng)`), `tessellate`, `curve-flow` (`InstancedFlow` offsets SET from `t`, never accumulated), `sprites`/`billboards`/`shader-particles`/`points-on-geometry` (sprite textures are `DataTexture`s — the corpus gate has no DOM), `volume-cloud`/`volume-perlin` (`Data3DTexture` from `util.noiseVolume`; steps scale with tier and double as `userData.count`), `dynamic-instances`. `constellation` is now the real `buffergeometry_drawrange` port (`nodeShape` point|sphere|cube, triangle-wave bounce so positions are pure in `t`). `util.ts` = `num/str/bool/countFor/softSprite/makeNoise3/noiseVolume`. `post` cards are 1-line markers (`pass: "<id>"`); the pass itself lives in `runtime/post.ts`, keyed by card id — the 14 post ids (`bloom`, `selective-bloom`, `film`, `vignette`, `smaa`, `sao`, `depth-of-field`, `chromatic-aberration`, `god-rays`, `pixelate`, `outline`, `sobel`, `dot-screen`, `ascii`) ALL drive real passes since Section 17 (before it only `bloom` did). Card params are what the pass reads; `parts` enum = diagram/title/props/all. `n8ao` renamed `sao`. Promoted from the business deck via `fx promote` (design D1): `agent-depth`, `capital-flows`, `cohort-grid`, `fab-wafer`, `geo-fragments`, `horizon-gates`, `proof-gate`, `trust-ledger` — carry no deck content; `cohort-grid` takes its split via `params.share`. `geo-fragments` plates are `depthWrite:false` (translucent overlap must blend) and therefore carry `renderOrder = 2` to sort ABOVE the floor veil (renderOrder 1): without it the veil's opaque far band passes its depth test against the floor behind the plates and punches a background stripe across the field at the horizon (business deck slide 15; background rows 5 -> 0 once fixed). This is enforced centrally by `runtime/scene.ts` `sortAboveVeil`, applied to every attached effect — effects do NOT set renderOrder themselves. Promotion re-ranks `backgroundForTopic` (cheapest-wins, ties by id): `geo`→`geo-fragments`, `agents`→`agent-depth`, `trust`→`trust-ledger`, `compute`→`fab-wafer`, `money`→`capital-flows`. Topic-world backgrounds `globe-arcs`/`city-grid`/`neural-mesh`/`vault-glyphs`/`server-racks`/`market-tape`/`orbit-agents`/`paper-stack` use `ctx.rng`, expose `object.userData.count`, scale counts by `quality`, and put their own depth offset on an INNER group (the runtime pins the returned object's z). See change: deck3d-cinematic-worlds. `video-screen` plays a clip on a framed screen in the scene: panel is UNLIT `MeshBasicMaterial` (a recording must keep its own pixels, not be tinted by the key light), bezel is lit metal and casts shadow, geometry re-fits to the clip aspect on first frame. Clip MUST be same-origin — a `file://` source taints the canvas and `texImage2D` refuses it; use `deck3d serve`'s asset route or `render --embed-video`. Playback is gated by `FxHandle.setActive`, driven by `runtime/index.ts` `settle()`, so a clip runs only while its slide is settled and a pinned time renders a stable frame. See change: deck3d-cinematic-worlds. `tokens`/`rings`/`swarm`/`particles` delegate to `runtime/backgrounds.ts`; the rest are permissive three.js-example ports behind the interface. |
| `compose.ts` | `composeEffects(effects, mode, quality, slideId, localCards?)` — a `local:<name>` id resolves through the embedded cards so per-deck effects enter the same gating. Also `LocalCards`. See change: deck3d-cinematic-worlds. Original: mode gating (skip + warn), conflicts, quality budget (low 6 / medium 12 / high 20). `validateEffectParams(ir)`: card-bound param checks with `overrides.slides["<id>"].effects[i].params.<p>` paths (E24). |
| `defaults.ts` | `defaultEffectsFor(slide, autoStyle?)` / `defaultSceneFor`: v1 precedence kept as a strict PREFIX (keyword → title-ish → `BY_DIAGRAM` for `flowchart`/`sequence` only), then a topic-keyword step inserted exactly where v1 fell to `particles`. `backgroundForTopic` picks the cheapest `background` card carrying the topic, ties by id. `autoStyle:false` restores the v1 fallback. See change: deck3d-cinematic-worlds. |
| `catalogue.ts` | Rows carry optional `topic[]` (drives `fx list --topic`). See change: deck3d-cinematic-worlds. `catalogue()` sorted rows, `renderCatalogue()` → `reference/effects.md`, `catalogueHash()` (drift detector). |
| `local.ts` | Per-deck effect resolution (design D1): `LOCAL_NAME_RE` `^[a-z][a-z0-9-]{0,63}$`, 64 KiB cap, card load + schema check, `kind ∈ {background,motion}`, sha256 pin, `lintModule` (leading `export default function`, single export, no `import`/`require`/`eval`/`Function` outside strings/comments), `toFactorySource`, `localRefs`. Used by `validate` (when the deck dir is known) and by `render` before embedding. See change: deck3d-cinematic-worlds. |
| `scaffold.ts` | `scaffoldLocalEffect` (writes a working stub + card, never overwrites, prints the pinned override entry), `hashLocalEffect`, `promoteLocalEffect` (both `--source`/`--licence` required, permissive-licence gate, corpus verify with rollback). See change: deck3d-cinematic-worlds. |
