Key takeaways
- Wire asynchronous work and application edits into the same frame-request path.
- Coalesce bursts but preserve requests raised during drawing.
- An idle queue is not proof that a scene has finished loading.
Wake for new data, not just camera movement
For a mostly static Spark splat viewer, connect SparkRenderer.onDirty to a coalesced frame request, and request a frame after your own visible changes. Rendering only when the camera moves can leave asynchronous results unseen. Keep an ongoing frame source while something is genuinely animating; idle rendering is not a replacement for animation updates.[4][6]

Spark announced its 2.3.0 on-demand-rendering improvements on X on September 30, 2026. The release notes carry a September 29 heading; 2.3.1 followed on October 1 with a build fix. This guide turns that change into a review workflow rather than a promise of faster rendering.[1][2][3]
Choose a viewer that can actually become still
An asset-review page, scene inspector or product configurator often spends time waiting for the next edit. That makes it a candidate for demand rendering. A game with moving characters, a running simulation or an animated camera has different needs. Three.js documents this distinction and shows why a continuous loop is appropriate for continuous motion.[6]
Start with one specific acceptance question: after the user stops interacting, can the page finish showing newly available content and then stop requesting frames? Make that question part of the handoff. Do not start with an arbitrary target frame count or battery-saving percentage.
Use a reproducible dependency baseline. The Spark quick-start page inspected on October 12, 2026 pairs Spark 2.3.1 with Three.js 0.180.0. That is a documented example pairing, not a claim that every other version is incompatible. Record the versions in your own project before changing its render loop.[5]
Give every visible change a route to the next frame
Spark starts and advances work during rendering. Sorting, level-of-detail selection, loading and paged data can finish later. Its onDirty callback requests another opportunity to render those results. Your application must also request frames for changes it makes itself.[4]
| Change | Frame request route | What to inspect |
|---|---|---|
| Spark asynchronous work | SparkRenderer onDirty | New data becomes visible without moving the camera. |
| Camera or canvas size | Control change / resize handler | Camera and projection updates are shown. |
| Object or material edit | Application edit handler | A stationary view reflects the new value. |
| Animation | An active animation update source | Frames continue while motion needs them. |
Treat an idle queue as a scheduling observation, not a loading-complete signal. Network requests, failed resources and pending application jobs need their own status and error handling. A quiet viewer can be correct, unfinished or broken; the frame counter alone cannot distinguish them.
Coalesce requests without losing the next one
The supplied scheduler is a small original teaching adapter. It accepts a draw callback plus scheduling and cancellation callbacks. Multiple requests made before a scheduled callback runs share one pending slot. It clears that slot before drawing, so a request raised inside draw can reserve the following frame. Its dispose operation cancels the pending callback and rejects later work.
That ordering matters. Clearing the pending flag after draw can erase a request made while drawing. Calling draw immediately from a change event can also create recursive calls when updating controls emits another change. Three.js illustrates this problem with damped controls and recommends scheduled, coalesced requests.[6]
Use the official Spark example to wire the real renderer; use this lab to reason about scheduling. Do not paste a second independent loop alongside an existing one and assume both are harmless. Identify which component owns the frame request and which components only notify it.
Run the offline frame-request lab
Download the files linked in the lab README, then run its Node test and report commands. No packages, network access, WebGL context or 3D assets are required. A controlled queue advances only when the fixture asks it to, making each requested and drawn frame inspectable. These are tests of the supplied helper, not tests of Spark or a browser.
- Burst requests: several changes before one queue step should cause one draw.
- A later completion: after the queue rests, a new request should cause another draw.
- A request during draw: the subsequent frame must remain pending.
- No new work: advancing an empty queue should not draw.
- Teardown: disposing with a callback pending must prevent later drawing.
The downloadable report records the results of ten actual Node tests on authored scenarios. Their event sequences are deliberately synthetic: they control when notifications arrive, not how long splat decoding, network traffic or GPU work takes. Run the tests before adapting the helper. These results are not FPS, power or loading-time measurements.
In React Three Fiber, use the existing invalidation system
For React Three Fiber, set Canvas frameloop to demand and route Spark onDirty to invalidate. Do not add the lab scheduler as a second frame owner. R3F coalesces invalidation requests; an invalidate call schedules work rather than drawing immediately.[4][7]
React prop changes can trigger demand frames, but imperative mutations can escape that mechanism. If an event handler changes an object directly, request invalidation after the change. Drei controls already integrate camera changes. R3F also documents a timing caveat for immediately starting animations: request the first frame before starting the synchronous animation on the next frame.[7]
Review the real scene before calling the change finished
- Load a scene, stop touching the controls and check that incoming content still appears.
- Change a material or object transform while the camera stays still; verify the change is visible.
- Resize the canvas and check its aspect and framing.
- Start and stop any animation; verify both motion and the return to idle.
- Navigate away during loading; check cleanup and console errors.
- Repeat with a slow connection and the browsers and devices you actually support.
This checklist is a proposed acceptance procedure, not a record of tests performed here. We did not run Spark, measure a renderer or compare devices for this article. Demand rendering does not reduce the cost of an individual draw, shrink an asset or establish correct transparency and sorting in your scene.
Spark v2.3.1 uses the MIT license, including notice-retention conditions. Check the rights to each scene and any redistributed dependencies separately; a renderer license does not establish permission to publish your input assets.[8]
Keep asset creation and viewer behavior separate
For a mesh-based prop workflow, Goblin3D offers Image to 3D and paid-plan GLB, OBJ and FBX exports. Review your chosen export before putting it into a viewer. This is a separate mesh workflow; these public features do not establish a Spark splat export or integration.[9]
The useful outcome is a viewer whose update paths you can explain: what changed, who requested the next frame and why it can stop again. Validate that behavior with your actual scene before discussing any performance benefit.
Evidence used
- Run the frame-request lab
Original dependency-free helper, deterministic scenarios and reproduction commands; not a renderer benchmark.
- Inspect the synthetic test results
Actual Node test results from authored controlled-queue scenarios.
- Use the event-wiring checklist
Source-linked wake-up routes and proposed real-viewer checks.
Sources
- 1.SparkJS 2.3.0 announcement — Spark. Accessed 2026-10-12.
- 2.Spark 2.3.0 release notes — Spark. Accessed 2026-10-12.
- 3.Spark 2.3.1 build fix — Spark. Accessed 2026-10-12.
- 4.Spark on-demand rendering — Spark. Accessed 2026-10-12.
- 5.Spark getting started — Spark. Accessed 2026-10-12.
- 6.Three.js rendering on demand — Three.js. Accessed 2026-10-12.
- 7.React Three Fiber scaling performance — Poimandres. Accessed 2026-10-12.
- 8.Spark v2.3.1 license — Spark. Accessed 2026-10-12.
- 9.Goblin3D public features — Goblin3D. Accessed 2026-10-12.
Create a mesh prop from a visual reference
Explore Goblin3D Image to 3D, then inspect the exported model in the viewer you choose.
Explore Image to 3DRelated Goblin3D guides and tools
Sources, product facts, and original evidence were checked before publication.