Tech

Optimizing 3D, Animation, and Chart Performance in an Expo Mobile App

A practical walkthrough of optimizing 3D visualization, 2D animation, and solar wind charts in a React Native (Expo) app.

Published Mar 26, 2026en-US
Optimizing 3D, Animation, and Chart Performance in an Expo Mobile App

When should we use beautiful 3D objects and animation? They are not just for visual flair. They are also highly effective when we need to communicate scientific concepts that users may otherwise find unfamiliar or difficult.

In an aurora app, user questions are straightforward: Can I see it now? Is the condition getting better? Do I need to move? That is why I split the UI the same way: global aurora conditions in 3D, local conditions through animation, and complex signals through charts. This post covers how I implemented that in a mobile environment and optimized it for stable operation.


1. 3D Earth with Three.js

Aurora Oval is a map that shows aurora observation probability. Most aurora apps use a 2D map, but I wanted to render that probability on a 3D globe for a more intuitive view. I used the following libraries to build and render 3D objects in Expo.

  • @react-three/fiber: Build a 3D scene with React components
  • three: Rendering core (camera, Material, Shader, etc.)
  • @react-three/drei: Utility helpers such as GLTF loading
  • expo-gl: Connect WebGL context in Expo
  • r3f-native-orbitcontrols: Touch rotation/zoom interaction control

At first, I tried to preserve the oval as close to the original data as possible for both fidelity and visual quality. But in production Android environments, stutter and performance issues caused app crashes. Here is a one-to-one mapping of causes and fixes.

  1. The raw source data used a 1-degree latitude/longitude grid. Rendering all 65,160 points directly caused a sharp increase in memory and rendering cost.
    • Fix: Normalize intensity values before 3D rendering, remove data below 20% probability, then apply grid-based compression to reduce point count.
  2. Even after that, too many points were drawn simultaneously, and brightness blending overlapped. The workload often exceeded the 60fps frame budget (~16ms), causing frame drops.
    • Fix: Cap per-layer points, total points, and concurrent energy levels to bound per-frame rendering work.
  3. Android WebGL environments have significant device-level context/driver variance, so burst load could trigger frame drops or context errors.
    • Fix: Design error handling with a failure-first mindset. Temporarily disable the aurora layer and retry automatically so the whole screen does not fail.

The main lesson here was clear: improving 3D rendering quality matters, but it must be designed together with performance measures that hold up in real operating environments.


2. 2D Animation with Reanimated and Svg

Tracking indicators used in aurora forecasting (solar wind, geomagnetic field, cloud cover) together can improve prediction accuracy around the user's location. However, for beginner aurora hunters, reading these values in real time is difficult. Instead of presenting raw numbers, I structured the view as a real-time 2D illustration that is easier to understand at a glance.

  • react-native-reanimated: Handle card/background/transition motion on the UI thread
  • react-native-svg: Render vector illustrations and particle layers
2D animated aurora map screen

A key advantage of react-native-reanimated is that animation computation runs on the UI thread (worklets), not the JS thread. So even when the JS thread gets briefly busy with network responses, state updates, or logging, frame loops for card transitions, background motion, and star twinkling remain relatively stable.

The strength of react-native-svg is expressive power and extensibility. Elements like aurora bands, solar-wind particles, and geomagnetic curves can be built precisely with Path, Line, Circle, and LinearGradient. Reusing the same shape structure also helps keep the 2D forecast screen consistent across changing intensity/speed conditions.


3. Solar Wind Chart with react-native-gifted-charts

  • react-native-gifted-charts: Render solar-wind time-series lines (speed/density/Bt/Bz) with pointer interactions
  • LazyMount + Suspense: Delay mounting heavy chart/illustration blocks and use skeleton fallbacks to distribute initial load cost
Solar wind chart screen

I chose react-native-gifted-charts because it quickly provides core chart features such as lines, axes, grids, and pointers. That let me focus on solar-wind interpretation UX instead of building a chart engine from scratch. It is also easy to extend with pointer events, line styles, and tooltip/overlay behavior based on screen intent.

For screen composition, I heavily used LazyMount + Suspense. Rather than rendering everything at once on screen entry, sections mount progressively based on data readiness and visibility. The same strategy worked not only for solar-wind charts but also for animations, illustrations, and other heavy views to effectively spread initial rendering cost.


Closing Thoughts

The biggest takeaway from this work was that cross-platform apps need performance design that adapts to diverse runtime conditions (OS and device variability). It is hard to solve everything at once, but the key is maintaining a practical balance between service stability and user experience.