State-Transition Dynamic Surface Effects

State-transition screen-space surface effects for Three.js WebGPU/TSL. Use for persistent touch, frost, or thaw history; full-field or sparse accumulation; static crystalline fields; reduced-resolution blur; or history-gated normal refraction.

Image: Touch-history Frost hardware WebGPU readback. Source lab: webgpu-touch-history-frost.

$threejs-dynamic-surface-effects 2 primary targets 1 secondary surface accepted runtime evidence Latest skill update commit 2ce13bc ↗ SKILL.md on GitHub ↗ raw (for agents) ↗

Evidence reports

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

The approach, mathematically

Surface history is a storage-texture ping-pong: touches write into a mask that decays and diffuses with correct time dependence, so behavior is identical at 30 and 144 fps:

$$m_{t+dt} = \operatorname{clamp}\Big(m_t\,k^{dt} + D\,\nabla^2 m_t\,dt + \sum_i T_i\Big)$$

where $k^{dt}$ (not a per-frame constant) gives exponential decay per second. Frost grows toward a static crystalline structure target $S(\mathbf p)$ — the mask reveals authored structure instead of accumulating mush:

$$f = \operatorname{smoothstep}(0, 1, m)\cdot S(\mathbf p)$$

Refraction reads the frost normal at two scales — coarse lensing plus fine crystal detail — offsetting the scene sample by $\Delta\mathbf{uv} = \eta\,(w_1 \mathbf n_{coarse} + w_2 \mathbf n_{fine})_{xy}$, with the blur radius driven by the same mask through a reduced-resolution node blur.

Accepted primary labs

Only schema-v2 labs with accepted runtime and evidence contracts appear here. Other source directories remain visible through the demo registry without being promoted to runnable proof.

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.

Accepted runtime evidence available10 published images
Native WebGPU runtime evidence preview

WebGPU Touch History Frost

Accepted
visualCorrectness
PASS
mechanismCorrectness
PASS
performanceCompliance
NOT_CLAIMED
gpuAttribution
NOT_CLAIMED
lifecycleStability
PASS
visualError
PASS
  • Correctness and hardware physical-route visual review pass on the current source closure.
  • Named-adapter GPU timestamp timing remains NOT_CLAIMED.

The full skill

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

State-Transition Dynamic Surface Effects

Own viewport-locked appearance history and its composite. Route world/object residue to $threejs-particles-trails-and-effects, weather accumulation to $threejs-rain-snow-and-wet-surfaces, bounded water to $threejs-water-optics, and shared full-frame output decisions to $threejs-image-pipeline.

When temperature, phase, loading, wetness, contact, or weather drives the appearance, close this local handoff before step 1:

  • name the physical quantity and units, producer frame/origin, transform and origin generations, and projection into screen UV;
  • name the source interval, cadence, sample phase, immutable producer/version, and renderer consumer;
  • declare support/filter, validity, staleness, error, and missing-signal policy;
  • bind the GPU resource generation, producing and consuming passes, and completion dependency;
  • keep the physical source read-only and give screen history its own appearance version; reset it on incompatible clock, origin, transform, projection, or resource-generation changes.

Invoke $threejs-choose-skills when feedback or additional system owners are required. The physical owner retains authority; screen-UV history remains renderer-owned appearance state. Then read external appearance signals for local projection and reset rules.

Process

1. Define the state transition and ownership

Write one contract for:

  • each history channel and its dimensionless meaning;
  • the screen-space event shape, timestamp, pressure/strength, and UV origin;
  • evolution terms such as decay, deposition, diffusion, or no change;
  • the state clock, suspension/seek policy, and reset dependencies;
  • the one history writer, composite consumer, and final output owner;
  • any external physical signal and the decorative overlay kept separate from it.

For touch/frost history, a useful split is visible coverage in R and a smoother tilt/refraction response in A; G/B remain unused or explicitly diagnostic. The step is complete when every channel has one equation, writer, unit domain, clock, and consumer.

2. Select update topology

Choose from the state transition rather than the visual theme:

Transition Update topology
Decay, diffusion, or forcing changes most texels Full-field ping-pong compute
Deposits are sparse and untouched texels are invariant Event bounds or dirty tiles
Sparse state also decays Timestamped tiles with analytic catch-up on every visible/filter sample, or materialize every visible tile
Many events overlap Bin events to tiles; bound/compact lists or rasterize one aggregate deposit field
State and inputs are unchanged Idle: retain history and dispatch nothing

Diffusion uses a full field or halo-expanded active domain. Read update topologies for overlap cost and diffusion stability. The step is complete when untouched texels evolve exactly as the chosen model requires and idle frames schedule no state work.

3. Make time and event integration invariant

Derive dt from two ordered samples on the same clock. Treat equal samples as zero elapsed time. A missing previous sample, reversal, seek, invalid mapping, or discontinuity follows the declared freeze, reset, analytic catch-up, or bounded-substep policy.

Rasterize timestamped pointer motion as swept capsules with aspect from the history texture. Integrate decay and saturating deposition together so a held pointer and a swept gesture converge independently of render-frame count. Subdivide event intervals only when their pressure or coverage variation exceeds the declared error gate.

Read dt-correct history before implementing decay, deposition, or diffusion. The step is complete when the same timed input yields equivalent history at 30, 60, and 120 Hz and a suspension does not silently discard elapsed evolution.

4. Allocate the ping-pong graph

Use WebGPURenderer from three/webgpu, call await renderer.init(), and require renderer.backend.isWebGPUBackend === true for this canonical path. Route explicit WebGPU-unavailable teaching to $threejs-compatibility-fallbacks. Allocate two renderer-owned StorageTexture histories at the lowest measured extent/format that passes reconstruction and quantization tests. Keep history, scene color, blur intermediates, and static structure fields as separate resources.

Order the graph:

immutable external appearance signals + timestamped UI events
  -> write next history from previous history
  -> publish/swap the completed history generation
single scene pass
  -> optional separable blur at its owned reduced extent
  -> static structure + current history composite
  -> optional history-gated normal refraction
  -> one RenderPipeline output transform

Use Fn().compute(...), storageTexture(), and textureStore() for history updates. Enqueue with renderer.compute(...); r185 computeAsync() is not a GPU-completion fence. Keep the steady path readback-free. Mark/rebuild the render graph when the output node, diagnostic route, resource extent/format, or quality branch changes.

Read resources and r185 execution before allocating or swapping textures. The step is complete when every pass reads a committed generation, no pass samples the texture it writes, and the scene is rendered once.

5. Compose blur, structure, and refraction

Generate static crystalline/noise fields once at startup or when their extent or quality changes. Treat them as data with NoColorSpace. Use separable blur at a pass-owned reduced resolution and alpha-aware normalization when alpha participates. Build the composite and refraction as TSL nodes feeding RenderPipeline.outputNode.

Keep scene and working buffers linear/HDR until the single output conversion. Use screen-period uniforms as periods; derive texel dimensions from the actual resource. Gate refraction by structural coverage and current history, define normal wrap and source-edge behavior, and apply Fresnel in linear light.

Read composite and refraction when either branch is active. The step is complete when blur extent, texture domains, UV conventions, refraction bounds, and output conversion are each owned and visible in diagnostics.

6. Close the lifecycle

Choose one resize policy: clear both histories, remap, or depth/camera-aware reprojection. Reset or reproject on incompatible clock, origin, transform, projection, resource generation, representation, or quality changes. Ordinary continuous source-version advances are sampled rather than reset.

On idle, retain persistent state and stop update work. On resume, apply the declared time policy before accepting new input. On disposal or device loss, invalidate the history generation and release both histories, blur/static resources, compute/pipeline nodes, and input/resize listeners; create a fresh generation before the next draw.

Read resize, reset, and disposal. The step is complete when resize, seek, quality change, idle/resume, disposal, and device loss each have an explicit state transition and resource result.

7. Prove state and presentation

Expose previous history, deposit, next history, dirty tiles/active texels, scene color, blur axes, static fields, pre/post-history mask, refraction offsets, final-without-refraction, and final. Add pause and single-step controls.

Measure event count, tile-list occupancy, dispatched texels, history and intermediate bytes, traffic lower bound, dispatch/draw timing, and whole-frame p50/p95. Scale by stopping idle work, choosing a valid sparse path, then reducing history/blur extent or precision after state-equivalence tests.

Read resource accounting and diagnostics and failure signatures. The skill is complete when timed inputs match across 30/60/120 Hz; full, sparse, and idle transitions behave as selected; ping-pong generations never alias; resize/reset/disposal controls pass; physical inputs remain read-only; the frame loop performs zero history readbacks; the scene renders once; output conversion has one owner; and the complete scene meets its named memory, traffic, and timing budget.

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.