FlappyChick is an endless game, but its software cannot be endless. Memory, draw calls, worker state, network requests, and deploy risk all need limits. Building for the browser forced us to treat every one of those limits as part of the game design.
This is the practical story of the v3.0.1 release candidate: a Phaser game built with TypeScript and Vite, delivered as static files, controlled with one button, and designed to keep generating a journey without letting the runtime grow forever.
We deliberately do not put “starts in under 2 seconds” in this title. A production launch-time claim needs repeatable cold-load measurements on the final public /play/ artifact, across documented devices and network conditions. Until that release gate passes, “fast-loading” is the accurate promise and the performance work below is the evidence.
Start with a browser-first boundary
The public game lives at /play/ as a standalone application. WordPress serves the homepage and articles, but it does not boot the game, own its physics, or sit in front of every frame. That separation gives the static game a simple path:
browser → CDN → index.html → hashed JavaScript, worker, CSS, images and audio
Core play does not require an account or a working API. Local best score and the run loop remain available if future profile or leaderboard services are unavailable. That is partly a resilience decision and partly a conversion decision: a player who follows a link should be able to play before creating anything.
Phaser runs the game; Vite packages the release
Phaser provides the rendering and scene foundation. Strict TypeScript keeps contracts explicit around physics, obstacles, world state, persistence, and input. Vite builds the same source tree for two different base paths: an internal beta at /play-beta/ and the public release at /play/.
The production artifact is static. Vite gives content-hashed entry files, while the configuration copies only approved runtime assets. Full-resolution chicken source files stay out of the deployed bundle. Source maps are useful during development but are not required in the public upload.
The largest current cost is JavaScript rather than server rendering. That makes caching effective, but it also gives us a visible optimization target: split nonessential code only when measurement shows the extra request boundary improves real startup, and keep art/audio additions accountable to the cold-load budget.
One button keeps input portable
Tap, click, and Space all map to the same flap event. The physics system does not need to know whether that event came from a touchscreen or keyboard. That keeps the core loop portable across mobile portrait, landscape, and desktop without maintaining separate versions of the game.
The input is simple; the simulation is not careless. A fixed physics step helps keep movement stable, while rendered rotation follows velocity for readable feedback. Ground and ceiling contact have a short recovery window, then behave as real boundaries. The rule is consistent across devices.
An endless journey built from bounded windows
The authored route moves through Village, Forest, Desert, City, and Mountain, then continues with lap variations. A seeded journey director tracks distance and the active biome. It keeps only a bounded window of relevant segments, while offscreen scenery and obstacles are destroyed.
That distinction matters. “Endless” describes what the player can experience, not the amount of state the browser should retain. Visible scenery tiles are proportional to viewport width. Weather and ambient effects use capped pools. At most two atmospheres are retained during a transition. Shared textures are created once and reused across restarts.
The result is a world that can continue changing without appending a permanent history of every tree, cloud, particle, and hazard the player has passed.
Plan obstacles away from the render loop
Mixed obstacles create a harder planning problem than repeating identical pipes. FlappyChick uses a Web Worker to plan upcoming obstacle groups and validate reachable corridors. The scene asks for approved plans ahead of the visible horizon, then materializes them only when there is still enough reaction distance.
The renderer owns presentation; authoritative obstacle geometry owns collision. Decorative inset shading can make a wall feel deeper without changing the fatal silhouette. If a planned group arrives too late, the game skips the full group instead of surprising the player with a hazard beside the chicken.
This architecture does not make difficulty automatically fair, but it gives fairness a place to be tested: seeded plans, corridor validation, reaction-time policy, and consistent hitboxes.
Mobile performance is a budget, not a toggle
The visual system has Low, Medium, High, and Auto quality modes. Lower tiers reduce ambient counts and optional detail while preserving the route, obstacle readability, and gameplay rules. Auto can step down after sustained slow frames; it never changes physics or difficulty.
Particle pools, weather pools, wisps, transition atmospheres, scenery tiles, listeners, workers, and timers all need explicit caps or cleanup. Repeated restart is a lifecycle test: the second run should not quietly cost more than the first.
Caching and deployment are part of game performance
Hashed static assets can be cached for a long time because a changed file receives a changed URL. The HTML entry document needs the opposite treatment: it should revalidate so it can point to the newest hashes after a release.
Deployment follows an asset-first, index-last sequence. New hashed files are uploaded and verified before the entry document changes. Older hashed assets remain available long enough for open sessions and fast rollback. If a release must be reversed, restoring the previous index can reconnect players to the retained build.
That discipline avoids a common static-site failure: a fresh HTML file that points to assets not yet present, or a stale HTML file that keeps an old build alive after deployment.
Measure the public build before making the headline claim
Build size, local preview timing, and a warm CDN response are useful diagnostics, but none proves a universal start time. For the public release we will record the exact commit and build ID, device/browser, network profile, cache state, sample count, median and slowest result, plus the event that counts as “started.”
That measurement belongs after the final /play/ artifact is live and the release gate has passed. Until then, the honest engineering claim is narrower: the game is static, cacheable, bounded at runtime, and designed to reach play with as little avoidable work as possible.
Want the player-facing side of the design? Read why one-button browser games create so much depth from a single action, or return to the FlappyChick homepage.
