Before web dev I made video games for about 5 years. Unity, C#, real-time multiplayer, frame budgets. When I started building org chart visualization software I didn’t think that background would be so relevant in performance optimisation front. Then our customers hit 10k, 30k, 240k employees and I was solving the same problems – just with DOM nodes instead of sprites and entities.
Here are specific techniques from game dev that made the biggest difference.
Don’t process what’s off-screen
Most fundamental optimization in games. If something isn’t visible to the player, skip it entirely. No rendering, no physics, minimal AI. Games call this frustum culling.
Our org chart is an infinite canvas. At any moment maybe 50-100 cards are visible, but the full tree might have 10,000+ nodes.
What we had before: render all 10k nodes, let the browser figure out visibility. 10,000 sets of DOM elements, Vue instances, reactive watchers – all for off-screen nodes.
What I did instead: bare-bones HTML placeholders for nodes outside the viewport. No Vue component mounted, no reactive tracking. When a node enters the viewport during panning, it gets instantiated on the fly. Vue’s async components made this clean.
For 10k nodes: ~100 fully rendered + 9,900 lightweight placeholders.
Before: 10k org → 1 min 45 sec, 5.5 GB
After: same org → under 5 seconds, 1.1 GB
Grid-based spatial partitioning
The viewport culling above has a problem. To decide which nodes are visible you check each one against viewport boundaries. 10k nodes × 60fps = 600k checks per second. Panning felt choppy.
Games solved this with spatial partitioning. Divide the world into a grid, each cell ~500px. To find visible objects, check which grid cells overlap the viewport — much fewer operations — then tell nodes in those cells they might be on screen.
Scales regardless of total node count. Whether 10k or 100k nodes, the number of grid cells overlapping the viewport stays roughly constant. Same thing every RTS does — StarCraft, Age of Empires, all of them.
Frame budgets and the render thread
In games you have 16.67ms per frame at 60fps. Blow that and the game stutters. Web browsers have the same constraint — block the main thread for 200ms and the UI freezes.
Our API sends compressed JSON. For 30k employees, decompression takes ~1.5 seconds on M1 Pro. That’s 1.5 seconds of frozen UI — no spinner, no animations, nothing.
In games the answer is obvious: move heavy work off the main thread. I moved JSON decompression to a Web Worker. Still takes 1.5 seconds, but loading animations play smoothly and the browser never shows “not responding.”
This also unblocked TanStack Query for background refresh — previously even background refetches caused micro-freezes during decompression.
The dictionary lookup pattern
Less “game dev technique” and more “thing anyone doing real-time systems learns the hard way.”
In games, looking up an entity by ID happens millions of times per frame. You never iterate an array to find something.
What we had:
// O(n*m) — 10k updates against 20k array = 200M comparisons
updates.forEach(person => {
const index = people.findIndex(p => p.personId === person.personId)
people[index] = person
})
What works:
// O(n+m) — build lookup once
const indexById = {}
people.forEach((person, i) => { indexById[person.personId] = i })
updates.forEach(person => {
const index = indexById[person.personId]
if (index !== undefined) people[index] = person
})
I found a production bug where .includes() on an array was called inside a filter loop for 20k employees. Browser ran out of memory. Dictionary lookup fixed it instantly.
The pattern
None of these techniques are new. Any Unity or Unreal developer would recognize them. But they’re rarely discussed in web dev because most apps don’t push the browser hard enough.
The moment your app becomes an infinite canvas — org charts, maps, design tools, data visualizations — you’re building something closer to a game engine than a web app. The constraints are the same: limited frame budget, finite memory, large worlds with smooth interaction.
I’d recommend looking at game dev resources before web performance blogs if you’re in that space. The game dev community has had 30+ years to figure these problems out.
| Technique | Web application | Impact |
|---|---|---|
| Frustum culling | Viewport-based instantiation | 20x render speed |
| Spatial partitioning | Grid-based intersection | Smooth panning at any scale |
| Thread offloading | Web Workers | UI never freezes |
| Level of detail | Simplified zoomed-out components | 10x fewer reactive bindings |
| Hash map lookups | Dictionary-based data access | Filters from 9s to 1.5s |
Leave a Reply