Procedural Fields

Procedural fields for coherent scalar and vector causes in Three.js WebGPU/TSL. Use when a task needs shared causes across materials, geometry, or compute; must choose direct evaluation versus a baked field; needs deterministic CPU/TSL parity and footprint filtering; or needs metric signed-distance terrain and coast analysis.

Image: final.design.png. Source lab: webgpu-field-bake.

$threejs-procedural-fields 1 primary target 1 flagship 1 secondary surface native evidence pending Latest skill update commit 2ce13bc ↗ SKILL.md on GitHub ↗ raw (for agents) ↗

Primary target surface

These rendering routes are generated from canonical source. Native-WebGPU acceptance remains separate from entrypoint availability.

Evidence reports

Source hashes, claim verdicts, promoted same-lab media, fixed routes, exact tier contracts, and current limitations.

The approach, mathematically

A field is a pure function $F:\mathbb R^3 \to \mathbb R^m$ from position to a bundle of channels (height, moisture, wear, mask…). Everything derives from shared causes, so channels correlate the way nature does. The workhorse is fractal Brownian motion over a noise basis:

$$F(\mathbf p) = \sum_{i=0}^{O-1} a^i\, n\big(f^i \mathbf p + \mathbf o_i\big), \qquad a < 1,\; f \approx 2$$

Domain warping feeds a field through itself to break up isotropy: $F'(\mathbf p) = F\big(\mathbf p + w\,\mathbf W(\mathbf p)\big)$. Derived surface data uses the gradient, e.g. slope masks $m = 1 - \hat{\mathbf n}\cdot\hat{\mathbf u}$ from

$$\nabla F \approx \frac{1}{2\epsilon}\big(F(\mathbf p + \epsilon\mathbf e_i) - F(\mathbf p - \epsilon\mathbf e_i)\big)_{i=1..3}$$

The contract the skill enforces: the CPU and TSL implementations are the same function (same basis, same seeds, same remaps), validated by GPU readback diff $\max|F_{CPU}-F_{GPU}| < \varepsilon$ — placement and shading must agree on the world.

Preview and evidence ledger

Every image identifies what it proves. Page screenshots demonstrate the published presentation only; generated inputs demonstrate asset channels only; rendering acceptance still requires same-lab readback and a schema-v2 bundle.

Canonical runtime evidence pending9 published images
Native WebGPU runtime evidence preview

WebGPU Field Bake

Evidence pending
visualCorrectness
PASS
mechanismCorrectness
PASS
performanceCompliance
INSUFFICIENT_EVIDENCE
gpuAttribution
INSUFFICIENT_EVIDENCE
lifecycleStability
INSUFFICIENT_EVIDENCE
  • Native WebGPU correctness bundle with field/storage/placement/probe readbacks and mechanism diagnostics.
  • Current-adapter GPU timestamps remain unaccepted in the correctness profile.

The full skill

The complete SKILL.md as loaded by agents, rendered verbatim.

Procedural Fields

Author one deterministic field bundle, then adapt it to each shader stage and consumer. Coordinates, seed, causes, derivatives, and output meanings belong to the field; fragment footprints, vertex LOD, storage, and diagnostics are consumer adapters.

For multi-system work, $threejs-choose-skills is an optional coordinator. Materials own PBR response, geometry owns topology, water owns free-surface state, and the final-image skill owns tone mapping and output conversion. Publish each cross-system field output with units, coordinate/frame convention, producer and consumer owners, revision, spatial/temporal support and filter, validity domain, error bound, update cadence, and staleness rule. Keep GPU resource layout and render LOD private to the consuming adapter.

1. Freeze the field contract

Record before implementation:

coordinate domain and units
seed owner, hash/noise family, and wrapping convention
primary fields -> derived causes -> named outputs
consumer, filter, precision, update cadence, and validity per output
direct-versus-bake decision inputs
debug view and invalidation key per output

Choose coordinates from the cause: world/tile-local for terrain and weather, undeformed radial physical coordinates for planets, object/local coordinates for ornament, or a declared generated-texture domain. Large domains use an integer tile plus local float (or another split representation) before f32 phase error changes the field.

This step is complete when every output has one coordinate/seed owner, units, consumer, filter, precision, update rule, invalid state, and diagnostic.

2. Author one stage-portable core

Implement the shared causes as a deterministic TSL Fn. Keep the core free of fragment-only derivatives, vertex camera policy, storage layout, and dispatch state. Materialize common intermediates once with .toVar() or an equivalent local; inspect generated WGSL when separate outputs might rebuild the same warp, hash, or octave chain.

import { WebGPURenderer, StorageTexture } from 'three/webgpu';
import { Fn, storageTexture, textureStore } from 'three/tsl';

Use WebGPURenderer and verify renderer.backend.isWebGPUBackend === true after await renderer.init(). renderer.compute() submits ordinary compute work. In r185, computeAsync() initializes before enqueueing but is not a GPU completion fence; completion evidence needs timestamps or an actual map/readback.

Carry parity-critical seeds as u32, not f32. Gate floored lattice coordinates to the i32 domain before bit reinterpretation. Apply a domain warp once to the coordinates, propagate its Jacobian into analytic gradients, and keep spherical warps tangent before renormalization.

This step is complete when all consumers call the same cause graph, each shared intermediate is evaluated once per invocation, and every stage restriction is isolated in an adapter.

3. Select direct evaluation or storage

Compare one amortization interval:

Tdirect = sum_i(Q_i * E_i)

Tbake = (D * (E_bake + Wstore) + Tdispatch + Tmips) / U
      + sum_i(Q_i * (S_i + B_i / BW_effective))

Q_i is the actual workload of consumer i; fragment workload is measured or modeled from resolution, coverage, MSAA/helpers, and overdraw. D is dirty texels, U reuse frames, and the remaining terms come from controlled target measurements. Choose the lower path subject to latency, memory, and error gates. Read count or octave count alone is not a decision.

For a bake, pack only channels sharing coordinates, precision, filter, cadence, and consumer locality. Use separate storage and sampling usage scopes; ping-pong when an update reads and writes the same logical field. RGBAFormat + HalfFloatType is the portable r185 rgba16float writable baseline (8 B/texel). Gate narrower/float32 formats and filtering against actual WebGPU features. Store local/normalized values rather than large absolute coordinates.

Read storage, precision, and invalidation before adding a StorageTexture, mip chain, dirty-tile updater, or category ID texture.

This step is complete when the chosen representation has measured cost inputs, exact bytes and simultaneous resource lifetime, a propagated per-channel error, and one explicit owner for creation, update, invalidation, and disposal.

4. Filter each consumer and add CPU parity only where needed

For post-warp footprint J' and a band's effective support frequency f_support, use

q = f_support * sigmaMax(J')

and require q <= 0.5 cycles/sample. Fade or prefilter before the bound, and transfer removed slope/normal energy to the material's variance/roughness path. Geometry displacement also obeys its mesh sampling bound. Fragment derivatives belong only to derivative-uniform fragment adapters; vertex and compute users need analytic, symbolic, supplied-footprint, or stored derivatives.

Create a CPU port only for geometry/offline generation, precomputed native tiers, or parity evidence. It imports the same constants, seed wrapping, normalization, and remaps as TSL. Compare direct f32, sampled/interpolated, and stored/quantized error separately; threshold decisions use a guard band rather than a global image tolerance.

Read filtering and parity when the field drives displacement, classification, a stored sample, or a CPU consumer.

This step is complete when every active frequency band passes the relevant screen/mesh footprint gate and every CPU/storage comparison states the error source, bound, and threshold behavior.

5. Wire consumers, invalidation, and diagnostics

Use NodeMaterial slots or geometry/compute inputs without recreating causes. Data fields use linear/NoColorSpace handling; authored color textures use SRGBColorSpace. Keep HDR buffers linear until the single output owner.

A field revision changes when its seed, coordinates, algorithm, source data, encoding, or accepted values change. Propagate that revision to dependent mips, contours, anchors, caches, and diagnostics. Static fields build once, slow fields update on cadence, edited fields invalidate dirty regions plus every filter/derivative halo, and all resources have a disposal owner. Frame-critical consumers stay on GPU or use an explicit asynchronous mirror; diagnostics may read back outside the render loop.

Expose source coordinates, each cause/band, derivatives or frames, packed channels, selected mip/footprint, validity, direct-versus-stored error, and the final consumer result. Use mrt() when views share the scene pass.

For metric signed distance, shorelines, cross-shore profiles, drainage, or contour-driving classifications, first read metric signed distance and coastal analysis.

This step is complete when changing any field input invalidates every dependent consumer, diagnostics expose every named output and failure state, and rebuilds produce the same values and identities for the same contract.

Completion

The field system is complete when one stage-portable cause graph accounts for every output; direct/baked and filtering choices are evidence-backed; CPU, storage, and threshold errors are explicit where those branches exist; all cross-system consumers receive versioned valid data in declared units; and coordinates, identities, mips, dirty regions, and resources remain stable through rebuild, LOD, resize, and disposal.

Secondary provider surfaces

Preserved concept proxies and generated-asset previews. They are excluded from primary completion counts and link to the canonical lab through the schema-v2 registry.