You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
floating-crates remains tracked by #522 with #573/#357 prerequisites. Closed spikes do not waive the original demonstrations, budgets, proofs or maintainer decisions below. Preserve the existing priority/order decisions; do not repeat S0/S1/S2 implementation.
Why
Trillion3D must handle water, fire and smoke natively, with a complete and fast base that stays inside a browser's limits. Jolt has no fluid solver, so this work adds a fluids layer beside it. It starts once the physics foundation #395 is merged.
These issues are the reference. The source brief (24 Sept. 2026) is closed. Its corrections, agreed with the maintainer, are built into the text below.
For the user, fluids are options of the existing API. There is no new concept:
Computed on the GPU, one way only, from physics to visual.
Never read back.
A third-party engine may be cited only as a point of comparison with numbers (costs, resolutions, particle counts). It is never the model this work follows.
Optimisation and performance rules (apply to every lot)
Fixed budgets. Fluids have their own GPU, CPU and memory budgets (budget.fluids), in three tiers: performance, balanced, high.
Exceeding a budget is reported, never silent.
A tier drop is the only automatic reaction allowed, and only for fluids. This is the AGENTS.md exception, written by the transversal lot.
Zero allocation per frame in steady state.
Every pool has a fixed capacity: particles, injection splats, fire lights.
Jolt objects are pre-allocated and reused in the physics thread.
No synchronous GPU → CPU read.
This covers readPixels, getBufferSubData, and mapAsync awaited inside the frame.
An asynchronous read is opt-in only, with at least one frame of latency.
Buoyancy is batched in C++.
The single wave model, written in TypeScript and running in the physics worker, computes one water plane per body. It adds those planes to the step's command batch.
One C++ function in our Jolt build walks the batch and calls ApplyBuoyancyImpulse, with Jolt's exact submerged volume.
No call per body, no work on the render thread.
One wave model. It is a pure maths module (TypeScript) used by buoyancy. It also generates the uniforms and the WGSL code, which are never written twice by hand. The previous frame's wave position (for temporal antialiasing) is the same formula at t - dt, so nothing is stored.
Visible water animates, like a playing clip. animate: false freezes it.
Far water updates its ripples less often.
Tier drops use the frame envelope, never a per-pass GPU duration. On tile-based GPUs, passes overlap (AGENTS.md).
Drop one tier only when the whole frame stays above its target for a full sliding window while fluids are visible.
Raise one tier only after a full window below the target, with a margin (hysteresis), so the tier does not oscillate.
Fluids' own share of the frame is measured at the bench, not in play.
Per-pass timestamps (WebGPU timestamp queries) only say where the time goes.
Pipelines compiled asynchronously and warmed up at load (createRenderPipelineAsync).
No frame over 33 ms the first time a body enters water or a fire is lit.
dt is clamped, and the renderer recovers cleanly after device.lost.
Temporal antialiasing integration.
Waves write motion vectors (t - dt).
Ripples and particles keep their previous state; for particles, that is one extra position.
A reactive mask limits history on fire and particles.
Scene depth comes from the existing visibility buffer.
Existing engine paths only.
Fire lights are light.point, inside the existing shadow budget; flicker only modulates intensity.
One lighting model for every surface.
Search before writing: reuse any engine sprite or particle path; the open world's rain and particles (moving to pasquelin/Trillion3D-openworld) may be read as prior art, never imported.
Designed for WebGPU default limits.
Storage buffers ≤ 128 MB, 256 invocations per workgroup, 16 KB of workgroup storage, 8 storage buffers per stage.
Larger limits are a bonus detected at runtime.
One renderer.
Every feature exists in WebGPU (WGSL).
Three.js and TSL are forbidden in the engine.
Own assets only. Flipbooks, caustics and VAT are generated by our own tool; nothing is taken from elsewhere.
Budgets (starting ceilings, confirmed or corrected by the S0 spike)
Parameter
performance
balanced
high
Total fluids GPU time (ms, measured at the bench on the envelope)
≤ 0.8
≤ 1.5
≤ 2.5
Fluids + buoyancy CPU time (ms)
≤ 0.3
≤ 0.4
≤ 0.5
Fluids VRAM (MB)
≤ 24
≤ 48
≤ 96
Gerstner waves
4
6
8
Local ripples
256²
384²
512²
Particles (total capacity)
16k
48k
128k
Simultaneous fire lights
2
4
8
Volumetric smoke grid
—
32³
64³
Active local simulations
1
2
4
Bench.scripts/mesure/banc, on the repository's public scenes, plus a fluids scene (one ocean, 100 floating bodies, 20 fires, 5 smoke volumes), on both renderers.
Acceptance criteria (MUST)
The budgets above hold at the bench, in every tier and on both renderers.
Water height differs by less than 1 cm between the CPU (buoyancy) and the GPU (render), at every sampled point.
No allocation per frame in steady state: the heap snapshot stays stable over 60 s.
No synchronous GPU read in the frame, checked by an instrumented wrapper in debug.
No frame over 33 ms the first time a body enters water or a fire is lit.
Buoyancy is bit-identical between one core and several: the same input gives the same Jolt trajectory. Safari and hosts without COOP/COEP run on one core.
Buoyancy runs entirely in Jolt's thread and adds no work to the render thread.
Nothing costs anything outside the physics active zone.
Under an exceeded budget, fluids drop one tier within 2 s, with a sliding window and hysteresis, no visible jolt and no oscillation. Only fluids are affected.
Lots
The four spikes come first. Their measurements are published as comments on their issues, with no report file and no dedicated branch. Then comes a STOP: the maintainer reviews S0–S3 before any production code. Every lot also ends with an explicit stop, awaiting validation.
Current scope — backlog reconciliation, 28 September
This is the fluids programme, not a second coding queue. The earlier comment “only S1 is done” is historical.
floating-cratesremains tracked by #522 with #573/#357 prerequisites. Closed spikes do not waive the original demonstrations, budgets, proofs or maintainer decisions below. Preserve the existing priority/order decisions; do not repeat S0/S1/S2 implementation.Why
Trillion3D must handle water, fire and smoke natively, with a complete and fast base that stays inside a browser's limits. Jolt has no fluid solver, so this work adds a fluids layer beside it. It starts once the physics foundation #395 is merged.
These issues are the reference. The source brief (24 Sept. 2026) is closed. Its corrections, agreed with the maintainer, are built into the text below.
For the user, fluids are options of the existing API. There is no new concept:
object.water(...),object.fire(...),object.smoke(...);obj.physics. A body floats on its own when its material's density is below the water's.Guiding principle (not negotiable)
Keep what affects the game apart from what is only seen.
A third-party engine may be cited only as a point of comparison with numbers (costs, resolutions, particle counts). It is never the model this work follows.
Optimisation and performance rules (apply to every lot)
budget.fluids), in three tiers:performance,balanced,high.AGENTS.mdexception, written by the transversal lot.readPixels,getBufferSubData, andmapAsyncawaited inside the frame.ApplyBuoyancyImpulse, with Jolt's exact submerged volume.t - dt, so nothing is stored.animate: falsefreezes it.createRenderPipelineAsync).dtis clamped, and the renderer recovers cleanly afterdevice.lost.t - dt).light.point, inside the existing shadow budget; flicker only modulates intensity.Budgets (starting ceilings, confirmed or corrected by the S0 spike)
Bench.
scripts/mesure/banc, on the repository's public scenes, plus a fluids scene (one ocean, 100 floating bodies, 20 fires, 5 smoke volumes), on both renderers.Acceptance criteria (MUST)
Lots
The four spikes come first. Their measurements are published as comments on their issues, with no report file and no dedicated branch. Then comes a STOP: the maintainer reviews S0–S3 before any production code. Every lot also ends with an explicit stop, awaiting validation.
AGENTS.mdexceptionThe order P1 → P2 → P3 is imposed. Water comes first, because it fixes the wave model and the contract with the renderers that everything else reuses.
Every lot ships its docs, its lesson and a live example, in every language. Every lot also carries its unit tests:
Out of scope
Links