Skip to content
Engineering Jul 4, 2026 · 7 min read

What Shipping 7 Browser Games Taught Us About Performance Budgets

GPU texture limits, asset budgets in real numbers, 60fps discipline, and the renderer crash that rewrote our rules — lessons from shipping seven games that run in a browser tab.

What Shipping 7 Browser Games Taught Us About Performance Budgets

Over the past year we shipped seven browser games — from Cosmic Glutton, a chill eat-and-grow idle game, through RICOCHET's one-shot bank-shot puzzles and DUSKWALKER's swing-and-release canyon runs, to GLOAM, our monochrome puzzle-platformer flagship that now lives on CrazyGames, Y8 and itch.io. Every one of them runs in a browser tab, with no install, no loading bar worth complaining about, and a hard 60fps expectation on hardware we do not control.

That last part is the real education. When you ship native, you pick a minimum spec. When you ship to the browser, your minimum spec is "whatever tab the player already has open, on whatever device, with whatever else running." The browser gives you an enormous distribution advantage — a link is the whole install flow — and in exchange it demands a discipline about performance budgets that most web work never forces on you.

We learned most of these lessons the honest way: by breaking things. The most instructive failure was not even inside a game. It was our own studio homepage, and it crashed Chrome outright.

The crash that rewrote our rules

For weeks we chased a bug where opening our site would kill the Chrome tab — not jank, not a slow frame, a dead renderer. The machine was an Apple M4 running Chrome's new Graphite rendering engine, so "weak hardware" was never a believable theory, though we wasted days on it anyway. We removed backdrop-filter from the header. We stripped big blurs and will-change hints. We converted every image to WebP and shrank decoded dimensions. We even disabled our custom cursor and an animated hero orbit on the theory that they were too heavy. None of it fixed anything, because none of it was the cause.

The actual cause was one CSS animation. Our homepage showreel duplicated 16 project cards twice — the classic infinite-marquee trick — into a single strip animated with transform: translateX. Thirty-two cards at roughly 320px each is about 10,240 CSS pixels wide. Harmless-sounding, until you remember devicePixelRatio. At DPR 2, the compositor has to rasterize that animated layer as a texture roughly 20,480 device pixels wide. The GPU's maximum texture size is 16,384 pixels. A layer that cannot fit in a texture cannot be composited. Safari responded by blinking the marquee in and out. Chrome's stricter Graphite engine responded by crashing the renderer.

The fix took ten minutes once we understood it: cap the showreel at 8 projects, bringing the track to about 5,100 CSS pixels — 10,200 device pixels at DPR 2, 15,300 at DPR 3, both under the ceiling. We verified by measuring the track's scrollWidth in a real browser rather than trusting our arithmetic.

The 16,384-pixel rule

Out of that came the first hard rule we now apply to every game and every site: any element that gets animated or transformed must stay under 16,384 device pixels in each dimension. In practical terms that means under 8,192 CSS pixels on a DPR 2 display and under 5,461 CSS pixels on a DPR 3 phone — and modern phones are exactly where DPR 3 lives, so the mobile budget is the real one.

The trap is that nothing warns you. The layer renders fine at 9,000 pixels, fine at 14,000, and then somewhere past the limit it silently fails or takes the tab down with it, depending on the browser. Wide marquees, long horizontal strips, oversized parallax backgrounds and giant SVG scenes are the usual suspects. We now measure the rendered width of anything animated, at the highest DPR we support, before it ships.

Asset budgets in actual numbers

Every game site we build gets an explicit asset budget up front, because "we'll optimize later" is how you end up shipping 40MB of PNGs. Our real numbers, for reference: Symbiosis ships about 540KB of images for an entire themed product site. Cosmic Glutton, which shows ten photoreal planets and eleven boss faces, comes in around 1.5MB. GLOAM is the outlier at roughly 11MB — but that is five short gameplay videos, and only the hero video is allowed preload set to auto; the other four use preload none and load when the player scrolls to them.

The techniques are boring and that is the point. Everything visual goes through white-to-transparent trimming and WebP conversion before it touches the repo. Sprite sheets get cropped to what is actually used. Video posters replace autoplay entirely for users who prefer reduced motion. When we need to squeeze an image or convert a batch to WebP, we use the image tools on our own Gigai Tools platform — they run entirely in the browser, which for game assets under NDA is not a small consideration.

60fps is a property list, not a wish

Sixty frames per second gives you 16.6 milliseconds per frame, shared between your JavaScript, style, layout, paint and composite. The only way we consistently stay inside it is by refusing to animate anything except transform and opacity. Both run on the compositor thread; almost everything else — width, height, top, left, box-shadow — triggers layout or paint on the main thread and will eventually miss frames on a mid-range Android phone even if your M-series laptop never blinks.

The corollaries we enforce: animated elements stay viewport-sized or smaller, reveals animate small elements individually rather than one giant container, scroll effects use IntersectionObserver instead of scroll handlers, and anything decorative respects prefers-reduced-motion by pausing videos and stripping autoplay, not just slowing down.

Build the kill switch before you need it

After the marquee crash we added a ?nofx URL parameter that disables all JavaScript-driven motion on our sites. It cost twenty lines and it has paid for itself repeatedly, because it converts "the site is broken on my machine" into a one-step bisection: if ?nofx fixes it, the problem is motion; if it does not, the problem is content. Every game and site we ship now carries an equivalent switch. When a player on hardware you have never seen reports a problem, you want a diagnostic they can run by editing a URL.

The browser deserves to be taken seriously

It is fashionable to treat browser games as a lesser tier, and we think that is out of date. WebGL and modern Canvas are fast, audio can be synthesized at runtime with no asset downloads at all — GLOAM's entire soundtrack is generated code — and offline play via service workers is routine. Portals like CrazyGames and Y8 put a playable link in front of millions of players with zero install friction. The trade is that you inherit the compositor, the texture limits, the memory ceilings and the thermal throttling of every device on earth, and you do not get to pick.

The rules we follow now

  • Any animated or transformed layer stays under 16,384 device pixels per dimension — budget for DPR 3, measure the rendered element, do not trust arithmetic.
  • Set an asset budget in kilobytes before design starts; our default is under 2MB of images per game site, video only when it earns its bytes and lazy-loaded past the hero.
  • Animate transform and opacity only; if a design needs anything else animated, redesign it.
  • Every reveal and scroll effect goes through IntersectionObserver; no scroll listeners doing style work.
  • prefers-reduced-motion is honored fully — videos pause, autoplay is removed, posters show.
  • Ship a ?nofx-style kill switch on day one, not after the first crash report.
  • Reproduce bugs on the strictest renderer you can find; the browser that crashes loudest is telling you the truth the lenient ones are hiding.

None of these rules came from a best-practices article. Each one came from a specific broken thing — usually broken in a way that only showed up on someone else's machine. That is the deal the browser offers: the widest reach any game platform has ever had, in exchange for treating performance as a budget you set on day one instead of a bug you fix at the end. Seven games in, we think it is a good deal.

Gigai Kripa Services

Web · App · Software · Game studio

See how we put this into practice across our products.

Explore our products →

Keep reading.