diff --git a/components/pinball/PinballCanvasImpl.tsx b/components/pinball/PinballCanvasImpl.tsx index e1229bb..8686ef5 100644 --- a/components/pinball/PinballCanvasImpl.tsx +++ b/components/pinball/PinballCanvasImpl.tsx @@ -74,6 +74,13 @@ export default function PinballCanvasImpl({ tableData, rom, tableInfo }: Pinball

)} + + {status === "starting" && ( +
+
+
STARTING TABLE
+
+ )}
); } diff --git a/lib/pinball/usePinballInstance.ts b/lib/pinball/usePinballInstance.ts index 352b88c..d5eee96 100644 --- a/lib/pinball/usePinballInstance.ts +++ b/lib/pinball/usePinballInstance.ts @@ -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; @@ -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 };