Building an open world in the browser, part 1: We started by trying to break it
By Oleg Sidorkin, CTO and Co-Founder of Cinevva
New here? Use the series guide. It explains what a spike is and links all parts.
Most open world projects fail in a very predictable order. First you get a nice concept. Then you get a pretty scene. Then you realize your frame budget is already gone before gameplay exists.
We wanted to avoid that trap. So we started with one hard test that could fail fast.
Spike 1 was not a trailer demo. It was a render budget check.
Open Spike 1 in a new tab ↗ · View source
The setup was simple on purpose. A 512 meter terrain mesh, procedural height from layered sine noise with island falloff, a water plane, atmospheric fog, and 500 instanced objects. We used plain WebGL with Three.js, ACES tone mapping, and no shadows.
The important part was not visual quality. The important part was whether the scene stayed stable while moving camera through it.
Two things came out of this spike that shaped the whole project.
First, we confirmed we had real headroom on desktop if we kept the first pass disciplined. That gave us confidence to attempt the harder terrain architecture later.
Second, we created a baseline contract. Every next spike had to explain its cost relative to this scene. If a new feature looked good but cost too much, it did not get promoted.
That baseline discipline became critical later when we hit seam artifacts, mixed LOD transitions, and compute driven meshing. Without a stable reference, every bug looks bigger than it is.
In part 2 we move from rendering to input feel. Worker physics sounds great in architecture docs. It only matters if the character still feels immediate when you press a key.
Technology referenced in this chapter
Heightmap terrain. A 2D grid where each cell stores a single elevation value. The GPU displaces a flat mesh in the vertex shader to create the terrain surface. Heightmaps are compact (a 65x65 chunk is ~8 KB at 16-bit), GPU-friendly, and fast to render. The limitation is that they can't represent caves, overhangs, or any surface that folds back over itself. For background on heightmap constraints and what comes after them, see our landscape generation guide.
Three.js. The rendering library we used throughout this project. Three.js abstracts WebGL 2 (and later WebGPU) into a scene graph with cameras, lights, materials, and geometry objects. It provides InstancedMesh for rendering many copies of the same geometry in a single draw call, frustum culling, PBR materials, and post-processing. See Three.js on GitHub. For how Three.js fits into a browser open world stack, see our browser 3D tech guide.
InstancedMesh. A Three.js feature that renders N copies of the same geometry with a single draw call, each at a different position/rotation/scale. The per-instance transforms are stored in a matrix attribute buffer. This is how we rendered 500 objects in Spike 1 without 500 separate draw calls. For vegetation at scale, GPU-driven instanced culling takes this further. See our landscape guide on GPU vegetation culling.
Frame budget. At 60 fps, each frame has 16.67 ms for everything: JavaScript logic, physics, rendering, and compositing. A "budget check" spike measures how much of that time a baseline scene consumes, leaving a known amount for features added later. This approach comes from AAA open world development where GTA V, Skyrim, and Elden Ring all use aggressive LOD and streaming to stay within fixed frame budgets.
Part 1 of 12.
Next: Part 2 - Worker physics and the input lag fear
Series guide: /blog/2026-02-25-open-world-browser-series-guide