Running the real game engine in the browser
Why the ZUPI demo is not a recreation, and what had to be true in the codebase for that to be possible.
Technical note · · 5 min read
Every product studio site faces the same problem: a screenshot cannot show you how something feels, and feel is most of what a game is. The usual answer is a recreation — a simplified browser version that approximates the product closely enough to be indicative. We think that is the worst available option. It costs real engineering, it drifts from the app the moment either changes, and the thing a visitor plays is, by construction, not the thing.
ZUPI let us avoid the choice entirely, for reasons that had nothing to do with marketing.
The boundary was already there
ZUPI’s rules live in a single self-contained module with no third-party runtime dependencies. It owns the physics, the collision handling, level generation, the difficulty curve, scoring and the three-second stillness rule. It does not know what a canvas is. It exposes a class you construct with a level number and step with a delta time, and it tells you where everything is.
That boundary was not drawn for the web. It was drawn so the game could be tested without a renderer and so the same rules could back an Android build and an iOS build without being written twice. It just happens that a module which can be driven by a test harness can also be driven by a browser.
What we actually did
- Vendored the rules module unmodified, with its provenance recorded next to it. Not a port, not a rewrite — the same file.
- Wrote a renderer for the web in NOVEMADE’s own visual language, rather than copying the app’s interface.
- Drove it from a fixed-timestep loop, so the simulation advances identically regardless of display refresh rate.
- Left level generation completely alone, so the board is level 1 as the app generates it.
The last point is the one that matters. Levels derive from a deterministic seed per level, so this is not a board that resembles level 1 — it is level 1. Beating it here means the same thing it means in the app.
Honesty as an interface problem
Because "the real engine" and "a recreation" are genuinely different claims, our demo catalogue carries a fidelity statement on every entry, and it is a required field in the content model rather than a nice-to-have. The demos that do not exist yet say what they will be when they do — including that Didi’s will have to be a recreation, because its logic is bound to its native UI layer and there is no engine boundary to expose.
The thing worth taking away
The reusable lesson is not about web demos. It is that "could someone else drive this without our UI?" is a question worth asking while a product is being built, not after. When the answer is yes, you get testability, portability and — as it turns out — a demo you did not have to build twice.