Infrastructure · We run it ourselves
Vite is a development server and build tool: it serves source files as native ES modules while developing, and bundles them with Rollup for production.
Data Nexus builds this site on Next.js 16 and builds PakiBus, a WebGL game with a post-processing pipeline, on Vite. The split is not preference: a framework earns its weight when the product is documents at URLs that must be rendered, cached and crawled, and earns nothing when the product is a single canvas holding sixty frames a second. Vite is what is left once the router, the server and the HTML pipeline are removed and only the module graph remains.
01
A framework is a bet about the unit of work. Next.js bets on the route: file-system routing, per-URL cache semantics, server components, streamed HTML, an image pipeline. A WebGL game has one document, one canvas and one main loop — there is no second URL for a router to resolve and no HTML for a server to stream. Every one of those subsystems still has to be configured, upgraded and worked around, and none of them has anything to bill against.
02
In development Vite does not bundle at all. Source files are served as native ES modules and transformed on request, so editing one shader or one post-processing pass invalidates that module and its importers rather than a route tree. This matters more for a game than for a page: tuning a bloom threshold or a camera easing curve is a loop of small numeric edits, and when each edit costs a rebuild the tuning stops happening — the value gets set once and left there.
03
three.js and a post-processing chain are large dependencies whose chunking decides time to first frame. Vite exposes Rollup's output.manualChunks and the rest of rollupOptions as ordinary configuration, so the split between engine code, shaders and game logic is a decision the team makes and can measure against a loading screen. Bundler configuration inside a framework belongs to the framework and moves underneath the project between minor versions.
04
vite build emits static files and nothing else — no server entry point, no Node runtime to boot. PakiBus ships through wrangler onto Cloudflare Workers, where a Worker serves those assets from the edge and no function cold start sits between a player and the first frame. A framework that can server-render tends to be deployed as though it must, and the cold start and the platform bill follow that assumption rather than the actual need.
05
PakiBus carries @vitejs/plugin-basic-ssl and vite-plugin-qrcode, and the reason is correctness rather than convenience. Real WebGL throughput, touch handling and device orientation exist only on a phone, and both pointer lock and DeviceOrientationEvent.requestPermission require a secure context, which a phone pointed at a plain http LAN address does not have. The SSL plugin gives the dev server a self-signed certificate and the QR plugin prints the LAN URL in the terminal as a code to scan, which turns testing on hardware into something that happens every session — the only way frame-rate regressions are caught while they are still cheap to fix.
06
Vitest reads the same Vite configuration: the same TypeScript transform, the same path aliases, the same environment replacement. The arithmetic that decides collision, spawn tables and scoring is therefore tested through the pipeline it ships through, and there is no second build configuration to drift out of step — drift between test config and build config is the usual reason a suite passes on code that fails in a browser.
07
lil-gui and stats.js are instruments, a parameter panel and a frame monitor, and neither may reach a player. Vite statically replaces import.meta.env.DEV during the build, so the branch that constructs them becomes dead code that Rollup removes, and the check is grepping dist for the string lil-gui. The instrument stays in the source where it is useful instead of being deleted by hand before every release, which is how frame monitors quietly disappear from projects.
An entry that cannot point at something you can open, run or read does not compile. That is a property of the type, not a promise in a paragraph.
Invariant
Vite has no router, no data layer, no server rendering and no metadata pipeline, so anything that must exist as documents at URLs — crawled, indexed, and read by clients that do not execute JavaScript — has to be assembled from a router, a head library and a prerender step, and that assembly is consistently worse than the framework it was meant to avoid. There is also a real correctness gap in the tool itself: development serves unbundled native ES modules while production is a Rollup bundle, so a dependency with broken ESM/CJS interop, or code that depends on import order or on a side effect that only fires when a module is fetched separately, can run for weeks in development and fail only at vite build. Every change has to be validated against an actual production build, not against the dev server, and teams that skip that discover it during a release.