valknarandClaude Sonnet 5 5b30442c4a Opt Big Shoulders out of automatic fallback-metrics adjustment
Next's fallback-metrics table has no entry for this (newer, merged
variable-font) family name, so the CLS-reducing fallback override it
tries to generate always fails with a build warning. Correctly
suppresses it under webpack; Turbopack (this project's default
builder) doesn't yet honor the flag the same way, so the warning
still prints there today, but the setting is still the semantically
correct declaration of intent and forward-compatible.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012hQoM3jJT1Lx7CMTciMzvD
2026-08-23 05:39:09 +02:00

vpinball

Play real .vpx pinball tables in your browser. Drop in your own table or try the bundled demo — no plugins, no native install. Powered by @valknar/vpinball-wasm, a WebAssembly build of the real Visual Pinball engine (real physics, real VBScript table scripting, real WebGL2 rendering).

Features

  • Play any self-contained .vpx table — drag a file onto the landing page, or click to browse.
  • Demo mode — a bundled table plays instantly with no upload required.
  • Fullscreen, a live FPS/stats readout, and a game info panel (table name, size, load time) in an in-game HUD styled after a pinball cabinet's coin door.
  • Installable as a PWA — add it to your home screen or desktop; the engine is cached after your first game so it's available offline afterward.
  • Deployable as a static site via Docker Compose — no Node server at runtime, just nginx serving prebuilt files.

Prerequisites

  • Node.js ≥ 20, pnpm ≥ 11
  • Docker + Docker Compose, for the containerized deploy

Install

pnpm install --frozen-lockfile --trust-lockfile

vpinball-wasm is published to a private Gitea npm registry (https://dev.pivoine.art/api/packages/valknar/npm/) under the @valknar scope. .npmrc maps that scope to the registry; the package is publicly readable, so no auth token is needed to install it.

To bump the engine to a newer published version:

pnpm add @valknar/vpinball-wasm@<version>

Development

pnpm dev

pnpm dev copies the engine's build output into public/vendor/vpinball-wasm/ first (via the predev script) — it's gitignored and regenerated from node_modules, so this only needs pnpm install to have completed. Re-run pnpm run copy-engine-assets by hand if you update the dependency without restarting the dev server.

The dev server runs at http://localhost:3000.

Build

pnpm build

Produces a fully static export in out/ (next.config.ts sets output: "export") — no Node server needed to serve it.

Docker deploy

docker compose build
docker compose up -d

Serves the static build via nginx on http://localhost:8080. No secrets or registry authentication are required to build the image.

PWA

The app can be installed from the browser's install prompt. A hand-rolled service worker (public/sw.js) precaches the app shell; the engine's .wasm/.data files (~34 MB) are cached on first visit to /play, not upfront, so installing the app doesn't force a large download before anyone has actually played. After that first visit, offline reloads work.

When you ship a release that changes the app shell or updates the vendored engine build, bump the cache version so returning visitors actually get the update:

pnpm run sw:bump

Controls

Key Action
Left Shift Left flipper
Right Shift Right flipper
Enter Plunger
1 Start game

On touch devices, an on-screen control overlay appears automatically once a table is running.

Limitations

Inherited from the underlying engine:

  • Only self-contained .vpx tables are supported — tables that reference an external .vbs script override or a Music/ folder won't load correctly.
  • No PinMAME, DOF, or FlexDMD plugin support.
  • Single-threaded (no SharedArrayBuffer) — performance depends on your device's single-core WebGL2 throughput.
  • Initial engine download is ~34 MB (cached after the first visit).
  • Gamepad input and DMD rendering are implemented but not yet confirmed on real hardware/tables.
  • Raw cabinet hardware input (real nudge/plunger boards) has no browser equivalent.

License

This app's own source has no license file yet — add one before distributing it. Separately, the built output bundles the @valknar/vpinball-wasm engine and its default table, which redistribute Visual Pinball source under a mixed legacy/GPLv3+ license (migrating file-by-file to GPLv3+ since October 2020) — see that package's own LICENSE for the authoritative terms before distributing a build.

S
Description
Play real .vpx pinball tables in your browser. Drop in your own table or try the bundled demo — no plugins, no native install.
https://vpinball.pivoine.art
Readme
356 KiB
Languages
TypeScript 85.6%
JavaScript 11.2%
CSS 2.3%
Dockerfile 0.9%