Repository navigation
Conversation
…kits keep cloud noise and sky between runs
Every bake group waits for the GPU's completion, about two frames, and a held startup draws no world until the bake ends: ten groups held the minimal kit about 350 ms on an Intel iGPU. Roughness-0 packets cost one sample, as the shader reads; the per-group budget doubles after a group the GPU finished by the next call, up to 4x the heaviest packet (the old largest submission), and falls back after a slow group. renderer_lifetime_test environment_bake_round_trips: 11 calls before, 5 after. The bench writes every TN_ marker beside a profile.
…nd add browser switches TN_LOAD_BENCH_TRACE=1 writes a whole-browser trace beside --cpu-profile, TN_LOAD_BENCH_INIT runs a probe script before the page, TN_LOAD_BENCH_CHROME_ARGS adds switches for one experiment.
…a ripples are built once - buildStaticColliders reads the lowest world Y from the position attribute and the matrix's Y row, the exact answer Box3.setFromObject(carrier, true) gave, without its per-vertex walk (35 ms of a minimal repeat visit). A rotated-sphere case locks the exact cull. - ctx.assets.memo accepts a build typed by an interface (the kits' ICloudTextures failed tsc). - The six atmosphere kits build the multiple-scattering table once a run, not once a refresh. - The collectathon keeps its sea ripple pixels in the sky's memo entry (118 ms a run).
…es without engine or array churn - Foliage composes instance matrices with Matrix4 and a YXZ Euler instead of a scratch Object3D, which crossed into the engine five times a placement (~90 ms of a load). Same matrix: 0 difference over 1000 random placements; only setMatrixAt crosses. - pathDistance loops over the path segments instead of building arrays and spreading them into Math.min: same minimum (0 mismatches over 200 000 points), 5x faster, ~65 ms of a load.
…not a trapped mirror Every engine Matrix3/Matrix4 answered 'elements' with a Proxy over a mirrored array that wrote each set element through writeDouble: three's compose wrote 16 trapped elements a call, 49 ms of a collectathon load on a laptop (~6k instance matrices). When engine memory is a SharedArrayBuffer (the threads build) it never detaches as it grows, so 'elements' is now a Float64Array view onto the matrix's 16 doubles: one store a write, live reads, no call. Unshared memory keeps the mirror.
…try chunk The web backend imported @dimforge/rapier3d-compat statically, so its ~2 MB base64 module (~660 KB Brotli) sat inside index.js: a first visit downloaded it before any engine code ran. The module is now a dynamic import started when physics is imported and awaited only by initialize(), so the bundler splits it into its own chunk that downloads while the engine's Wasm compiles. A caller that inits Rapier itself and hands over a World still works: the module binds when the import resolves.
…ures download A first visit painted the sky (~600 ms on a laptop) only after loadSurfaces resolved, and the main thread sat ~50% idle for ~500 ms waiting on those downloads. Both now run under one Promise.all. The load bench can profile the first visit (TN_LOAD_BENCH_PROFILE=first).
…n Rapier A first visit's waterfall showed the network idle until the entry script ran: the engine glue asked for its 1 MB Wasm at ~330 ms (done ~800 ms), and Rapier's chunk waited until ~900 ms for the engine to boot. The web build now writes a head script that preloads the Wasm the page will run (the threads build only when the page is isolated) and, once it lands, modulepreloads Rapier, so each download gets the whole link in turn. The load bench can dump resource timing (TN_LOAD_BENCH_RESOURCES=1).
…ser has not fetched c12's preload made a repeat visit ~150-200 ms slower on the laptop: a preloaded response loses V8's compiled-code cache, so the 4 MB module recompiled every visit. The head script now records the Wasm URL when its preload lands and skips the preload (and Rapier's) when that URL is recorded, so a repeat visit fetches through the HTTP cache as before. Storage that throws counts as unrecorded.
… hashes, no delete in the memo spec
…LOAD_BENCH_SHOTS)
Contributor
Author
…ems once before the next engine call
…ce the engine made them
…develop; status PARTIAL with the two open ACs
… wait for the owner
…er, same bytes The sRGB byte comes from 255 exact steps, not a power a texel; azimuth terms and the ripple lattice are computed once; path and pickup distances skip what is clearly out of reach and keep Math.hypot at the edge. skyPixels, ripplePixels and the whole built course hash the same before and after.
rapier() and recast() no longer await their Wasm in setup when the runtime offers holdStart: each leaves a start gate, so load() runs while they initialize and enter() still waits until ctx.physics is set. A physics node built before then throws TN_PHYSICS_NOT_READY, not the deprecated-world message. With no holdStart, setup awaits as before.
…load # Conflicts: # packages/create-threenative/__tests__/scaffold.spec.ts
jonit-dev
marked this pull request as ready for review
November 9, 2026 03:41
jonit-dev
added this pull request to the merge queue
Nov 9, 2026
Any commits made after this event will not be merged.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.




What is this PR about?
A web game should open fast on the first visit and almost at once on the next one. On develop the collectathon kit is ready in 3.90 s on a first visit and 2.51 s on a repeat visit (20 Mbit/s, 40 ms, Intel Iris Xe laptop, Brotli served). This PR adds a load benchmark (
pnpm bench:load) and cuts startup work until the benchmark shows it. Each change stays only when a paired laptop run shows a gain.Why does it matter?
Players on the web leave while a game loads. Most of the gains are in the engine and the web build, so every game built with ThreeNative gets them with no code change.
Where it stands (laptop medians, develop and this branch paired in one window, three rounds each)
The owner asked to merge with AC-1 open. The PRD stays PARTIAL.
What landed
ctx.assets.memo); hashed assets areimmutable.InstancedMesh.setMatrixAt/setColorAtwrite the instance array and send it once before the next engine call, not one engine call an instance (-130 ms on a repeat, paired).Float64Arrayview, not a Proxy that writes each element (49 ms of the collectathon load).index.js864 → 250 KB Brotli).Box3.rapier()andrecast()initialize while the scene loads;enter()still waits untilctx.physicsis set (start gate). A body built inload()throwsTN_PHYSICS_NOT_READY(-64 to -179 ms first visit, paired).Measured and dropped (numbers in the PRD): bake beside the compile, a JS pipeline precache, Rapier as a streamed
.wasmalone, two readiness gate changes, an always-on Wasm preload.Checklist
Phase 1 — benchmark
pnpm bench:loadreports first-visit and repeatreadyMsPhase 2 — a repeat run does not redo work
Cache-Control: immutablectx.assets.memostores generated arrays; collectathon builds through it (kit playtests on the merged tree: 37 pass; only the no-hitch GPU p95 rows fail, as on develop for collectathon and minimal; racing 18.63 ms has no develop control)Phase 3 — a first visit downloads less
Acceptance
PRD:
docs/PRDs/performance/PRD-860-a-repeat-web-run-is-ready-in-about-a-second.md