Add vpinball: a browser Visual Pinball player

Next.js app that plays real .vpx tables via @valknar/vpinball-wasm
(WebAssembly Visual Pinball). Demo mode with the bundled default
table, drag-and-drop custom table upload, a cabinet-styled UI with a
coin-door HUD (fullscreen/stats/info/eject), a custom 404 page, PWA
support (hand-rolled service worker), and a static Docker Compose
deployment behind nginx.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012hQoM3jJT1Lx7CMTciMzvD
This commit is contained in:
2026-08-23 05:25:15 +02:00
co-authored by Claude Sonnet 5
commit e493a12e68
51 changed files with 5570 additions and 0 deletions
+35
View File
@@ -0,0 +1,35 @@
"use client";
import { useEffect, useState } from "react";
/** Self-contained rAF-based FPS counter — the engine exposes no perf API of its own. */
export default function StatsReadout() {
const [fps, setFps] = useState<number | null>(null);
useEffect(() => {
let frameCount = 0;
let windowStart = performance.now();
let raf = 0;
const tick = (now: number) => {
frameCount += 1;
const elapsed = now - windowStart;
if (elapsed >= 500) {
setFps(Math.round((frameCount * 1000) / elapsed));
frameCount = 0;
windowStart = now;
}
raf = requestAnimationFrame(tick);
};
raf = requestAnimationFrame(tick);
return () => cancelAnimationFrame(raf);
}, []);
return (
<div className="flex items-baseline gap-2 rounded border border-chrome/30 bg-black/40 px-3 py-1.5 font-mono">
<span className="text-xs tracking-widest text-chrome">FPS</span>
<span className="text-lg tabular-nums text-arc">{fps ?? "--"}</span>
</div>
);
}