Dev log · BuildCity Dev Log

Running a 3D city on a low-end PC: the white-screen fix and the lightweight recovery mode

What broke on 4 GB machines, how the loading screen now waits for a real first frame, and what the recovery mode turns off.

Articles are currently available in English.

The report: a white screen and nothing else

In early September 2026 we received several reports with the same shape. A player opens their city, the loading screen fades, and then nothing: a white page, no error, no way back except reloading. The reports came from older laptops and budget desktops rather than phones, which was the first clue, because the mobile path already ran a reduced rendering profile and the desktop path did not.

We never obtained a crash log from the affected machines. What follows is the set of failure paths we found by reading the startup code and reproducing them under controlled conditions, and the changes that shipped on September 11. We were careful then, and are careful now, not to claim that every white screen had the same cause.

Four ways to get a white screen

  1. The low-spec check only ran on mobile. The client builds a memory profile at startup that decides how many vehicles to allocate, whether to load citizen models, and whether to enable post-processing. A PC with 4 GB of RAM or four or fewer cores was getting the full desktop profile, which allows up to 1,200 vehicles, citizen models, and the full post-processing chain.
  2. The lightweight graphics option could not actually disable post-processing. A condition elsewhere forced the effect composer on, so even a player who had found the setting was still creating the extra render targets.
  3. The loading screen closed before the first frame was drawn. If WebGL then failed during the first real render, the error UI tried to reopen a loading screen that had already been dismissed and could not.
  4. Context loss had no durable fallback. When the browser took the WebGL context away, the game showed a brief toast, resized on recovery, and otherwise carried on, with no path back to a basic renderer if post-processing kept failing.

Individually each of these is a corner case. Together, on a machine with little memory, they form a straight line from "open city" to "blank page."

What changed

The low-memory and low-core profiles now apply to PCs as well as phones, and to anyone who picks the lightweight mode explicitly. That profile turns off shadows, post-processing, citizen models, and the up-front vehicle mesh allocation, and uses the same reduced object limits mobile already used. Machines that report no hardware information at all, and ordinary PCs, keep the profiles they had.

The loading screen now waits for a real first frame: it closes only after the renderer has issued actual draw calls and the WebGL context is healthy. If rendering fails, or the context is lost, the game shows a persistent, plain-HTML notice and stops the main render loop instead of spinning on a dead context. When the context comes back, the next successfully drawn frame dismisses the notice. The notice lives outside the game module entirely, so it works even when the renderer could not be created in the first place.

Post-processing became fail-safe. If initializing or rendering the effect chain throws, the passes and render targets are disposed and the game falls back to direct rendering. If that keeps failing, the notice stays up rather than the page going white.

The lightweight recovery mode

The notice offers a button labeled, in each supported language, to reopen the city with lightweight settings. It appends a recovery flag to the current city URL and reloads. We deliberately did not make it overwrite your saved quality preset or trigger an automatic reload loop; it is a one-time retry you choose. In recovery mode the client loads 3D models one at a time instead of several in parallel and skips the background warm-up of the full building catalog, while still pre-loading the minimum residential models that new zones need to grow. That last exception comes from an earlier regression we documented: a low-end profile that skipped both the initial bundle and the background warm-up left new zones with no model to grow into, and the fix was to guarantee a small backbone set regardless of profile.

Two things the update does not include, by design: automatic resolution scaling, which exists in the client but is off by default because players told us they would rather keep their chosen resolution and accept a lower frame rate, and broad model level-of-detail switching, which would be a much larger change to the asset pipeline.

What it costs

Every change in this project is weighed against server load, client memory and GPU work, and network traffic, and this one is unusual in being almost entirely a reduction. There are no new server endpoints, database queries, or background jobs. On low-end machines the client allocates fewer render targets, no shadow maps, and fewer dynamic objects at startup. The normal rendering path only gained a context health check and a branch; the recovery UI and its objects are created only when an error actually happens. Network usage is unchanged in the normal case and lower in recovery mode because non-essential model warm-up is skipped. We did not measure the exact RAM and VRAM savings on real low-end hardware, and said so in the change notes.

How we tested without the machines

Seven hardware-profile cases exercise the classification: a 4 GB PC, a low-core PC, a normal PC, a browser that exposes no hardware information, an iPad reporting a desktop user agent, the explicit recovery flag, and the pre-existing low-end option. A set of browser tests drives the real client code in a headless Chromium with every network request intercepted by fixtures, so nothing touches the server, and covers initialization failure, render failure, an empty first frame, post-processing teardown and disposal, context loss and recovery, the race between the loading screen's fade-out and the notice, a 360-pixel-wide viewport, and that the recovery URL preserves the city address. For context loss we create a genuine software WebGL context, deliberately lose it with the browser's own extension, restore it, and read back a green pixel to confirm the renderer is drawing again.

None of that is a substitute for the actual laptop that produced the report. A browser that cannot create WebGL at all, or an operating system that kills the tab outright, is beyond what a fallback inside the page can handle. For those cases the GPU help page walks through enabling hardware acceleration in each browser.

If it happens to you

  • Use the lightweight retry button on the notice. It keeps your city and your settings.
  • Check the GPU help page if the notice says the browser could not create a graphics context at all.
  • Lower the render resolution or quality preset in settings before loading a very large city on a weak machine.
  • Tell us through the contact page with your browser and device if the lightweight mode also fails. A real log from an affected machine is still the thing we most want.