Matter Field: one authored field for GPU (TSL) + CPU consumers - is this a useful contract?

This mostly came out of building Objekt 62

It has a ~40 km procedural dune world rendered through Three.js/TSL, while gameplay needs immediate CPU queries against that same terrain for collision, placement, vehicles, slope, etc. I ended up with the terrain math authored once against a small adapter surface so JS gameplay and the TSL renderer weren’t maintaining two different versions of the world.

That made me wonder if the pattern itself was useful enough to extract, and also where the assumptions in it actually stop being true.

Matter Field is that experiment.

The deal is deliberately narrow: author a closed-form field once against a restricted math vocabulary, then materialize that same definition as JS f64 for gameplay/Workers/server use, native TSL for WebGPU/WebGL2, and dual numbers for gradients.

The other part is that I don’t assume shared source means CPU/GPU float equality. It doesn’t. So there is an f32 diagnostic path plus hardware-scoped drift receipts over named samples instead of a “parity = true” claim.

Restriction is basically the point. No arbitrary JS, no data-dependent branches inside the field, no pretending every operation has portable-enough behavior just because both sides can spell it.

TypeGPU is obviously the closest overlap. I tried that path too and there is an optional TypeGPU integration in the repo. For a WebGPU-first app doing broader compute/resources/pipelines, TypeGPU may just be the better answer. Matter Field is testing a narrower contract around fields specifically: native TSL + WebGL2 fallback, immediate JS queries, generic CPU gradients, a restricted numeric surface, and measured drift.

I also don’t think this beats the normal approaches universally. If a texture/heightmap can be the authority, great. If the formula is small enough that maintaining CPU/GPU twins isn’t painful, also fine. Readback can be fine if latency doesn’t matter.

The case I’m interested in is more specifically: a procedural field evaluated densely on the GPU, queried sparsely and synchronously by gameplay, and complicated/changeable enough that having two independent implementations starts to feel sketchy.

Current examples are terrain/contact, swell/buoyancy, wind/forces, and a moving queryable volume. The receipts are finite regression evidence on named hardware, not global bounds, and they don’t claim the analytic field equals the rendered triangles between vertices either.

Matter Field source code

Live examples / assays

Objekt 62, which is where the architecture came from:

What I’d value from this forum specifically: is this actually a useful contract once you’ve lived with this problem, or is one of the standard approaches basically always the better engineering choice? Also very interested in what field/op/workload immediately makes the restriction not worth it.

1 Like

The restriction is the right instinct, but I think it is guarding the wrong thing. In my experience the vocabulary is rarely what breaks parity — argument magnitude is, and a 40 km world is exactly where that starts to bite.

Where the drift actually lives

f32 has a 24-bit mantissa, so the spacing between representable values at x is about x · 2⁻²³. At the edge of a 40 km world that is roughly 5 mm — fine for terrain height. But procedural fields do not evaluate at x. They evaluate at x · frequency, once per octave. An octave at 512× on a coordinate of 40 000 lands at ~2·10⁷, where f32 spacing is about 2 units of the noise domain. On the CPU in f64 that same octave is exact to well past the point of caring.

So the divergence between your two consumers is not a uniform epsilon you can quote once. It is near zero for the low octaves, garbage for the high ones, and it grows with distance from the origin. A drift receipt gathered near the origin will look excellent and tell you nothing about the far tiles — which is where the vehicle will end up.

The standard fix is the boring one: rebase coordinates before the field sees them, so the field is always evaluated near the origin and the large offset never enters the transcendental. Worth deciding whether that rebasing lives inside your contract or outside it, because it changes what “the same definition” means on the two sides.

The second source, which no vocabulary restriction can remove

Both GLSL and WGSL specify the transcendentals — sin, cos, exp, pow, inverseSqrt — with a bounded ULP error, not a fixed result. Two vendors can both be conformant and disagree, and they do. Any field built on trig noise inherits that disagreement no matter how narrow the vocabulary is, and it is the one term you cannot make deterministic by authoring it once. Marking expressions precise on GLSL backends stops contraction into FMA, which removes a different and smaller part of the gap; it does nothing for the transcendental tolerance.

That is not an argument against the contract. It is an argument for the diagnostic path being the product and the parity claim being the thing you never make — which is roughly what you already concluded, and I think you underrate how much of the value sits there.

The metric I would change

Drift over named samples measures the field where you mostly do not care. For collision and placement, the quantity that decides whether the game is broken is the sign disagreement rate in the band where |f| is within one voxel of zero — how often the CPU says inside and the GPU says outside on the same point. Mean absolute drift can be tiny while that number is bad, because error concentrates exactly where the field cancels, and that is the isosurface.

The gradient path deserves its own number for the same reason. Differentiating amplifies the discrepancy roughly in proportion to 1/|∇f|, so on the flat pans of a dune field — where the slope query matters most for vehicles — the two normals can disagree by more than the values ever do.

On the contract question you actually asked

I think it earns its keep exactly while the field stays closed-form, and that condition tends to expire. The moment there is an erosion pass, a hand-painted mask, or a baked heightmap tile, the definition stops being authorable once and the adapter surface has to grow a sampler — at which point you are back to two versions of the world, only now with an abstraction in between.

So the question I would want answered before extracting it is not whether the pattern is useful. It is where the baked data is going to live when it arrives.

Two things I would genuinely like to know:

  • What is the sign-disagreement rate in the near-zero band, at a far tile rather than near the origin?
  • Is coordinate rebasing inside the contract or outside it?

I work on browser 3D commercially — parametric and field-driven rather than authored geometry, so this is the corner I live in. The closest public thing I have on this exact subject is a graded gyroid lattice ray-marched from a distance field: no mesh, no texture, geometry straight out of a function. It is linked from my thread in Jobs (#93376) rather than here, since the forum will not take the host from a new-ish account.