Show a spinner while the table is starting

instance.start() runs synchronously and blocks the main thread for a
noticeable moment on heavy WASM init, so pressing "Tap to Start" would
freeze on the old screen with no feedback until the HUD suddenly
appeared. Add a "starting" status between "ready" and "running", and
defer the actual start() call by a nested rAF so the browser gets a
chance to paint the spinner before the blocking call runs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-24 09:52:30 +02:00
co-authored by Claude Sonnet 5
parent d7ea38d8ee
commit 53c8ea03ac
2 changed files with 20 additions and 3 deletions
+13 -3
View File
@@ -10,7 +10,7 @@ import { hasWebGL2 } from "./webglSupport";
// outside webpack's module graph (see scripts/copy-engine-assets.mjs).
const ENGINE_BASE_URL = "/vendor/vpinball-wasm";
export type PinballStatus = "idle" | "loading" | "ready" | "running" | "error";
export type PinballStatus = "idle" | "loading" | "ready" | "starting" | "running" | "error";
interface EngineModule {
loadPinball(options: LoadPinballOptions): Promise<PinballInstance>;
@@ -114,8 +114,18 @@ export function usePinballInstance(
const start = useCallback(() => {
if (!instanceRef.current) return;
instanceRef.current.start();
setStatus("running");
setStatus("starting");
// instance.start() runs synchronously and blocks the main thread for a
// noticeable moment (heavy WASM init) — a nested rAF lets the browser
// paint the "starting" spinner from the state update above before that
// block hits, instead of the UI freezing on the old screen mid-click.
requestAnimationFrame(() => {
requestAnimationFrame(() => {
if (disposedRef.current || !instanceRef.current) return;
instanceRef.current.start();
setStatus("running");
});
});
}, []);
return { status, progress, error, loadTimeMs, instanceRef, start };