sparksbud

Quest WebXR Performance

A desktop world that feels effortless can miss every headset frame. Sparksbud taught us to stop treating one scene counter as a performance verdict. Geometry, draw calls and textures help locate a problem, but the actual question is whether the headset presents both eyes on time while the visitor turns, opens the dock and changes worlds.

At a requested 72 Hz, a frame has about 13.9 milliseconds before the next display interval. Treat that as a target, then measure on the headset. Sparksbud tracks triangles, draw calls and geometry counts, builds reduced detail for selected worlds and checks two-eye rendering in an emulator. Those checks do not establish physical Quest frame pacing or thermal behavior.

Begin with the display interval

Divide one second by the refresh rate to get the interval: 72 Hz is about 13.9 ms, 90 Hz about 11.1 ms and 120 Hz about 8.3 ms. That interval is a target for the complete frame, including application work, rendering and the browser's XR path. It is not a promise that an app requesting a rate will get it. Sparksbud asks for 72 Hz when the session reports that rate as supported; the actual rate still needs a headset readout.

The renderer draws each eye from a different view. A desktop screenshot shows one view and does not exercise that stereo path. On Sparksbud we check that both eyes render, that the XR dock remains usable and that scene counters stay within each world's own budget. A Quest emulator is useful for geometry, control and rendering regressions. It runs on a desktop GPU, so its frame timing cannot stand in for a mobile headset.

The 72 Hz WebXR frame target and its evidence A 72 Hz display interval is about 13.9 milliseconds for the entire frame. Scene counters diagnose the work; a physical headset frame-time measurement decides whether the target is met. A frame budget needs a device measurement At 72 Hz, the whole display interval is about 13.9 ms. Application update + both eye views + browser XR work No fixed share belongs to any one part. Measure the complete frame. Diagnostic counters Triangles, calls, geometry Find what changed in the scene Headset frame time Turn, interact, switch, repeat Decide on the target device
The 72 Hz WebXR frame target and its evidence At 72 Hz the complete display interval is about 13.9 milliseconds. Scene counters show where to investigate; headset frame time checks the result. Measure the whole frame 72 Hz = about 13.9 ms One display interval Update + both eye views + browser XR work No fixed slice per part Scene counters Triangles, calls, geometry Find what changed Headset frame time Turn, interact, switch Decide on the target device
The 72 Hz interval is about 13.9 ms for the complete frame. The figure separates measurements we can collect in a browser from the physical headset timing that decides whether the world is ready.
What each number can tell us
SignalUseful forCannot prove
Rendered trianglesFinding an unexpectedly dense scene or quality profileGPU time on a headset
Draw callsSpotting too many separate objects or materialsSmooth frame pacing by itself
Geometry and texture countsCatching growth across switches or long sessionsThat every allocation has been released
Desktop frame intervalsComparing local builds under the same conditionsQuest FPS or battery and heat behavior
Physical headset frame timeAcceptance on that device, build and scenePerformance on every other headset

Three.js exposes render calls, triangles and allocated geometry through renderer.info. Sparksbud copies those counters onto its canvas for diagnostics. We reset the render counters at each draw because a stereo frame and a postprocessing frame are not comparable unless we know what was counted. We also record the number of XR eye cameras. That makes a report more useful than “it looked smooth on my laptop.”

Write down the refresh rate reported by the headset before judging a frame budget. Requesting 72 Hz in code doesn't prove the session runs at 72 Hz.

Spend less on what does not change the scene

Sparksbud shares geometry through instancing, merges some static shapes and keeps scene motion in bounded pools. The cheap version of a world is authored, not an emergency blur filter. Its quality choice can cut geometry, particle counts or shader work while retaining the composition. Kaleidoscope has a separate Quest profile because the full desktop optics were too heavy in headset testing. The desktop version stays intact, while the headset takes the lighter build.

Some Sparksbud worlds also lower desktop pixel ratio after sustained slow frames. That path runs only outside XR. The headset path uses the browser's XR framebuffer and a world-specific scale setting before the session begins. Three.js documents that setFramebufferScaleFactor() cannot be changed during an XR session, so it is not a control to turn reactively after each slow headset frame.

Foveated rendering also has a visual cost. Sparksbud currently sets foveation to zero because a view-locked edge showed a contrast change in tiny additive particles as the head tilted. That is a tradeoff specific to these scenes, not a general recommendation to disable foveation. A developer should compare the actual peripheral result and headset frame time before deciding.

Find the cost before cutting the sceneTriangle and draw-call counters are diagnostic clues. They do not substitute for timing the full stereo frame on a headset. Find the cost before cutting the sceneDifferent bottlenecks need different changes.01Scene objectsCount calls andgeometry.02Both eye viewsCheck pixel andshader work.03Main threadTime build andupdate work.04Visual resultKeep the world’sfocal point.
Find the cost before cutting the sceneTriangle and draw-call counters are diagnostic clues. They do not substitute for timing the full stereo frame on a headset.Find the cost before cuttingthe sceneDifferent bottlenecks need differentchanges.1Scene objectsCount calls and geometry.2Both eye viewsCheck pixel and shader work.3Main threadTime build and update work.4Visual resultKeep the world’s focal point.
Triangle and draw-call counters are diagnostic clues. They do not substitute for timing the full stereo frame on a headset.

Make the budget repeatable

  1. Capture a named build, device, browser version, scene and quality profile. “Quest profile” in a desktop browser is only an emulation of scene choices.
  2. Use the same viewpoint and interaction. A world seen toward open sky may cost less than a frame facing the densest geometry or the XR dock.
  3. Record frame time over more than the first few seconds. Shader compilation, scene changes and heat can change the result.
  4. Keep CPU-side and GPU-side hypotheses separate. Fewer triangles will not fix a long JavaScript build on the main thread; fewer objects will not fix an expensive full-screen shader.
  5. Recheck the visual result. An optimization that removes the world's main focal point is a failed design change even if the counter improves.
A world review sequence
StageEvidenceDecision
BuildFinite geometry, bounded instances, no broken shadersThe scene is structurally safe to render
BrowserDesktop, narrow phone view and two-eye emulatorLayout, controls and stereo path behave
HeadsetPhysical frame time while turning and interactingThis device can present the world acceptably
Long sessionRepeated switches and time in sceneHeat, memory and frame pacing stay acceptable

The first two stages are good at catching mistakes cheaply. They cannot close the last two. Our QA records mark physical headset pacing as unmeasured where it has not been read. The world lifecycle note covers the repeated-switch side of the budget.

Repeat the same headset routeMeasure the same scene and interaction on the physical device over time, then check the visual result as well as frame time. Repeat the same headset routeA frame-time number needs its conditions beside it.01Name the buildRecord device,browser and profile.02Use one viewRepeat the same turnand interaction.03Measure over timeInclude switches anda longer session.04Review the artCheck that theoptimization stilllooks right.
Repeat the same headset routeMeasure the same scene and interaction on the physical device over time, then check the visual result as well as frame time.Repeat the same headsetrouteA frame-time number needs itsconditions beside it.1Name the buildRecord device, browser andprofile.2Use one viewRepeat the same turn andinteraction.3Measure over timeInclude switches and a longersession.4Review the artCheck that the optimization stilllooks right.
Measure the same scene and interaction on the physical device over time, then check the visual result as well as frame time.

Keep the camera on the densest part of the scene and repeat once with the dock visible and once hidden. Record those as separate runs instead of averaging them together.

Where the next gains may be

The largest apparent meshes are not always the slowest pass. A full-screen shader runs over pixels in both eyes, while an instanced scene can submit many triangles in few calls. If a world misses its target, profile the problem before cutting art. Our next measurement pass should use physical Quest frame time in the densest view, with the dock shown and hidden, then decide whether to change geometry, overdraw, textures, framebuffer scale or the update loop.

Questions developers ask

Is 13.9 ms a guaranteed Quest budget?

No. It is the interval for 72 Hz, not a measured application allotment. Browser, compositor and device behavior must be measured on the target headset.

Can I use a desktop Quest profile as a headset benchmark?

No. It can check which content and quality path loads. It does not reproduce the headset GPU, thermals or frame timing.

Does a lower triangle count always improve VR performance?

No. Shader cost, overdraw, textures, draw calls and JavaScript can dominate a frame. Change the measured bottleneck and check the visual result.

Can framebuffer scale change mid-session in Three.js?

Three.js says setFramebufferScaleFactor() cannot be used during an XR session. Set it before entry, then verify the actual result on the device.

Why is foveation disabled in Sparksbud?

Tiny additive particles showed a view-locked edge artifact as the head tilted. That observation is specific to Sparksbud's art; other scenes may benefit from foveation.