Performance Budgets for VR: Hitting 90fps or Paying the Sickness Tax
Miss your framerate target in VR and users won't just experience lag—they'll experience nausea. Here's how to build and enforce performance budgets that keep your VR app smooth.
Performance Budgets for VR: Hitting 90fps or Paying the Sickness Tax
There's a hard truth about VR development: a desktop game running at 45fps might feel sluggish. A VR experience at 45fps will make your users sick. The human vestibular system is unforgiving. When what your eyes see doesn't match what your inner ear expects, your brain registers threat. Nausea follows.
This isn't hyperbole—it's physics. And it's why performance budgets in VR aren't optional. They're survival requirements.
Why 90fps Matters More Than You Think
The motion-to-photon latency in VR is typically 20–30ms. If a user turns their head, the display needs to reflect that movement within that window. Miss it, and you create a disconnect between movement and visual feedback. That's the recipe for simulator sickness.
90fps gives you roughly 11ms per frame. On current-gen headsets (Meta Quest 3, PlayStation VR2), it's the baseline expectation. Drop to 72fps and you're at 14ms—seemingly small, but enough to trigger discomfort in prolonged sessions.
The Math Is Unforgiving
Let's say your target is 90fps on Quest 3:
- Total budget: 11.11ms per frame
- CPU time: ~5ms (physics, logic, input)
- GPU time: ~5ms (rendering)
- Overhead: ~1ms (OS, driver)
That's it. You're not getting more.
Building Your Performance Budget
Start concrete. Profile your actual target hardware, not an estimate.
pythonimport time class PerformanceBudget: def __init__(self, target_fps=90): self.target_fps = target_fps self.frame_time_ms = 1000 / target_fps self.cpu_budget = self.frame_time_ms * 0.45 self.gpu_budget = self.frame_time_ms * 0.45 self.overhead = self.frame_time_ms * 0.1 def check_frame(self, cpu_time, gpu_time): if cpu_time > self.cpu_budget: return f"CPU over budget: {cpu_time:.2f}ms / {self.cpu_budget:.2f}ms" if gpu_time > self.gpu_budget: return f"GPU over budget: {gpu_time:.2f}ms / {self.gpu_budget:.2f}ms" return "Frame OK" budget = PerformanceBudget(90) print(budget.check_frame(4.2, 5.8))
Break It Down by System
Don't lump everything together. Assign budgets to subsystems:
- Physics: 1–2ms
- Spatial audio: 0.5ms
- Input/tracking: 1ms
- Scene culling: 1ms
- Draw calls: 2–3ms
- Post-processing: 1–1.5ms
If physics alone takes 3ms, you've already cut your rendering time by 40%. This forces prioritization.
Monitoring in Production
You can't ship and hope. Instrument your builds.
typescriptclass FrameProfiler { private marks: Map<string, number> = new Map(); mark(label: string) { this.marks.set(label, performance.now()); } measure(label: string, startMark: string): number { const end = performance.now(); const start = this.marks.get(startMark) || 0; const duration = end - start; if (duration > 5.5) { console.warn(`⚠️ ${label} exceeded budget: ${duration.toFixed(2)}ms`); } return duration; } }
At LavaPi, we've found that teams that instrument early catch budget violations before they hit QA. The cost of optimization is exponentially lower when you're fixing subsystems, not the entire pipeline at the last minute.
The Real Cost of Missing It
Yes, some users tolerate frame drops. But tolerance isn't acceptance. A single project that ships with chronic motion sickness complaints will damage trust in your brand and the VR medium itself.
The performance budget is your contract with the user's physiology. Honor it, and your app feels responsive, immersive, alive. Break it, and you've built a nausea machine.
Think of it this way: every frame you drop is a user who'll put down the headset. Every frame you maintain is an hour of engagement you keep.
LavaPi Team
Digital Engineering Company