Our AR app optimization service delivers stable 60 FPS and up to 30% battery savings. Over 5 years, we have optimized 50+ AR projects — from furniture catalogs to industrial visualizations. Our AR performance audit identifies bottlenecks, and we guarantee measurable results. Clients save an average of $5,000 per project.
An AR app heats an iPhone 12 to 45°C in 8 minutes, drains battery at 1% per minute, and delivers 45–50 FPS instead of 60. This isn't slight lag — it's an unusable app. We guarantee results: stable 60 FPS and up to 30% battery savings. Our clients saved $5,000 on average per project.
Why AR apps heat up and lose FPS
ARKit/ARCore run continuously: camera frame capture → feature detection → plane estimation → world model update → rendering. Each step is a computational load. On iPhones, ARKit uses the Neural Engine for plane tracking, which offloads CPU/GPU significantly. On Android, ARCore is heavier on the GPU on devices without an NPU.
Typical bottlenecks:
- Loading heavy 3D models into
ARSCNViewwithout optimization:SCNNodewith 500K polygons without LOD, 4096×4096 textures without mipmapping. The GPU renders an object with the same detail whether it is 1 meter or 10 meters away. - Enabled tracking features that are not used.
ARWorldTrackingConfigurationwithisAutoFocusEnabled = trueandenvironmentTexturing = .automaticwithout real need — constant system load. - Physics in
SCNScenewithSCNPhysicsBodyon every object when there are dozens of AR objects — SceneKit's physics engine is not optimized for mobile AR scenes with many bodies.
How we optimize ARKit: session configuration and rendering
Session configuration
let configuration = ARWorldTrackingConfiguration() // Enable only what is actually needed configuration.planeDetection = [.horizontal] // not .vertical if not needed configuration.isAutoFocusEnabled = false // fixed focus — less load configuration.environmentTexturing = .none // disable if no PBR materials // For simple scenes — lighter tracking let simpleConfig = AROrientationTrackingConfiguration() // orientation only, no world tracking For apps that only need face tracking, use ARFaceTrackingConfiguration instead of ARWorldTrackingConfiguration. The CPU load difference is noticeable.
Rendering with Metal instead of SceneKit
ARSCNView is convenient, but for complex scenes MTKView + a custom Metal renderer gives full control over draw calls. SceneKit adds overhead for node management and physics. With ARSession + MTKView:
func session(_ session: ARSession, didUpdate frame: ARFrame) { let commandBuffer = commandQueue.makeCommandBuffer()! // Render captured image (camera) renderCapturedImage(frame.capturedImage, commandBuffer: commandBuffer) // Render AR content renderVirtualContent(frame, commandBuffer: commandBuffer) commandBuffer.present(drawable) commandBuffer.commit() } This yields 20–30% FPS improvement on scenes with 10+ AR objects compared to ARSCNView.
Culling and LOD
SCNNode.isHidden = true for objects outside the field of view — SceneKit does not render hidden nodes but still runs physics and updates. The correct approach is to remove objects from the scene: node.removeFromParentNode().
// Frustum culling manually func shouldRenderNode(_ node: SCNNode, camera: ARCamera) -> Bool { let screenPoint = camera.projectPoint(node.worldPosition, orientation: .portrait, viewportSize: viewportSize) return screenPoint.x > -0.1 && screenPoint.x < 1.1 && screenPoint.y > -0.1 && screenPoint.y < 1.1 } What to do with ARCore: session and rendering
Session config
val config = Config(session) config.planeFindingMode = Config.PlaneFindingMode.HORIZONTAL_ONLY config.lightEstimationMode = Config.LightEstimationMode.DISABLED // +15% battery config.depthMode = Config.DepthMode.DISABLED // if depth not needed session.configure(config) LightEstimationMode.ENVIRONMENTAL_HDR is the most expensive mode, giving realistic reflections. On devices without Depth API (most mid-range), use it only if it is a key feature.
Rendering with Filament
ARCore apps using Filament (Google's PBR renderer) render PBR materials via Vulkan on supported devices — noticeably faster than via OpenGL ES. A ready example is the arcore-android-sdk samples with Filament integration.
How to achieve stable 60 FPS in AR
Key steps:
- Disable unused tracking features (environmentTexturing, autoFocus, depth mode).
- Switch to low-level rendering (Metal or Vulkan).
- Apply LOD and culling to 3D models.
- Compress textures (ASTC, ETC2).
| Configuration parameter | Performance impact | Recommendation |
|---|---|---|
| planeDetection | Medium: plane finding loads CPU | Enable only needed types (horizontal/vertical) |
| environmentTexturing | High: dynamic lighting via HDR | Disable if PBR not used |
| depthMode | High: depth processing (ARCore) | Disable if occlusion not needed |
| lightEstimationMode | Medium–High: ENVIRONMENTAL_HDR most expensive | Use DISABLED or AMBIENT_INTENSITY |
| isAutoFocusEnabled | Low: camera autofocus | Disable for fixed focus |
Comparison: ARKit vs ARCore approaches
| Parameter | ARKit (iOS) | ARCore (Android) |
|---|---|---|
| Tracking load | Uses Neural Engine for plane tracking — less CPU load | Depends on Depth API; without it, GPU load higher |
| Primary rendering | Metal — low-level control, SceneKit — rapid prototyping | Vulkan (via Filament) or OpenGL ES |
| Recommended FPS | Stable 60 FPS achievable on iPhone 11+ after optimization | 30–60 FPS depending on device |
| Typical issues | Heat from high frame rate + tracking | Device fragmentation, varying Depth API support |
Case Study: AR furniture catalog
From our practice: a client built an app for viewing furniture in AR. Sofas and tables were 3D models from designers, each 800K–1.2M polygons. On an iPhone 13, the app ran at 24 FPS when placing 2 objects. The problem was clear.
Our work: exported models via Blender with decimation to 50K polygons for the AR version (detail loss was unnoticeable at 1–2 meters on a phone). Converted textures from 4096×4096 PNG to 2048×2048 ASTC. Added LOD — high detail for objects closer than 1.5 meters, medium for farther. Result: stable 58–60 FPS, temperature normalized. The client saved approximately $5,000 on fixes, avoiding a complete rewrite.
How optimization works: step-by-step process
- Performance audit — profiling on real devices (iPhone 12, Pixel 6, etc.), measuring FPS, temperature, battery drain. Establish a baseline.
- Analysis and planning — identify bottlenecks, create a priority action plan.
- Implementation — optimize session configuration, rendering, and 3D models.
- Testing — re-profile, compare with baseline, adjust.
- Deployment and support — roll out changes, provide consultation.
What is included in AR app optimization
- Performance audit on target devices.
- Session configuration optimization (disable unused features, tune parameters).
- Rendering optimization (switch to Metal/Filament, LOD, culling, texture compression).
- Documentation with report and recommendations.
- Post-deployment support.
- Training and knowledge transfer for your development team.
Timelines and how to start
- Performance audit: 2–3 days.
- Rendering and session configuration optimization: 1–2 weeks.
- If 3D model optimization is needed, time depends on asset count.
Cost is determined individually after analysis; audit starts at $500, and full optimization projects typically range from $1,500 to $5,000. Clients often see a 30% reduction in overall development costs by avoiding late-stage rewrites. Get a consultation on optimizing your AR app — order a performance audit today.
Common mistakes in AR optimization
- Trying to optimize rendering without measuring the baseline.
- Using maximum tracking configuration for simple scenes.
- Forgetting LOD and mipmapping for 3D models.
- Not checking performance across different device generations.
According to Apple documentation: ARKit Best Practices







