# Interactive Solar System 3D Viewer in SwiftUI

> By Lawrence Arya, Founder & CEO of VP0. Published 2026-06-07. 5 min read.
> Source: https://vp0.com/blogs/interactive-solar-system-3d-viewer-swiftui

Orbits are rotations, not trigonometry. Texture sells it, and nothing watchable is ever fully to scale.

**TL;DR.** An interactive solar system viewer in SwiftUI uses RealityView with RealityKit (the modern path; Model3D for a single USDZ, SceneKit only for legacy): a parent Sun entity, each planet a child whose orbit is the rotation of an invisible pivot rather than per-frame trigonometry, with moons nesting the same way. Texture is what separates it from colored marbles, NASA albedo maps, an emissive Sun, Saturn's rings as a textured alpha disc. No watchable model is fully to scale, so pick the scale cheat deliberately and label it. Tap-to-focus plus an info panel turns the screensaver into an app. A free VP0 design supplies the cards and controls to build the scene under.

## What renders the planets, and what is the honest scope?

RealityKit on the modern path, SceneKit on the legacy one, and the scope question matters before either: a solar-system viewer is gorgeous and finite, so decide early whether it is an accurate model or a beautiful toy, because the two have different data and different physics. Both are legitimate; pretending a toy is accurate is the trap.

[RealityKit](https://developer.apple.com/documentation/realitykit) is Apple's current 3D framework and the right default for a new build: it renders physically-based materials, runs on iOS and visionOS, and drops into SwiftUI through `RealityView`. SceneKit still works and has more tutorials, but it is the aging path. For a quick single-model drop-in, SwiftUI's [`Model3D`](https://developer.apple.com/documentation/realitykit/model3d) loads a USDZ with almost no code; for an interactive system of orbiting bodies you want the full `RealityView` with entities you control.

## How do you build the system itself?

As a parent-child entity hierarchy where orbits are rotations, not position math. The clean structure: an anchor holds the Sun, each planet is a child entity, and a planet's orbit is the rotation of an invisible parent pivot around the Sun rather than per-frame trigonometry on x and y. Spin the pivot, the planet circles; rotate the planet entity itself, it spins on its axis. Two transforms, no orbital math, and moons nest the same way under their planet.

```swift
RealityView { content in
    let sun = ModelEntity(mesh: .generateSphere(radius: 0.3),
                          materials: [sunMaterial])
    for planet in planets {
        let pivot = Entity()                 // orbit center
        let body = ModelEntity(mesh: .generateSphere(radius: planet.radius),
                               materials: [planet.material])
        body.position = [planet.orbitRadius, 0, 0]
        pivot.addChild(body)
        sun.addChild(pivot)
        // animate pivot.orientation around Y for the orbit
    }
    content.add(sun)
}
```

Texture is what separates a credible viewer from colored marbles (and the size gap is real: Jupiter spans about 139,820 km against Earth's 12,742 km): each planet gets an albedo map (NASA publishes usable surface imagery, and [NASA's solar-system resources](https://science.nasa.gov/solar-system/) are the canonical reference), the Sun gets an emissive material so it glows rather than reflects, and Saturn's rings are a textured disc with alpha, not geometry. Keep sphere mesh detail moderate; a viewer rendering eight high-poly planets plus moons can stutter, and level-of-detail (simpler meshes when zoomed out) is the budget that keeps it smooth, the same render-only-what-you-must discipline as [the flight radar live map](/blogs/flight-radar-live-plane-map-overlay-react-native/).

## Where does the "accurate vs. beautiful" fork actually bite?

In scale, and you cannot have both. Real relative sizes and distances make the planets invisible specks across an empty void (Jupiter is tiny next to the Sun; the gaps are enormous), so every watchable solar-system viewer cheats, and the honest move is choosing the cheat deliberately:

| Model | Sizes | Distances | Best for |
| --- | --- | --- | --- |
| Visually balanced | Compressed, all visible | Compressed, navigable | The toy that teaches shape and order |
| Size-accurate | True relative radii | Compressed | Showing how small Earth really is |
| Fully accurate | True | True | A specks-in-void art piece, rarely usable |

State the choice in the UI ("distances not to scale") rather than implying a fidelity the model does not have, the same estimate-labeling honesty that governs every data visualization. Orbital speeds can follow the same logic: real relative periods (Mercury fast, Neptune glacial) are cheap to encode and add real teaching value even when sizes are stylized.

## What makes it interactive instead of a screensaver?

Gestures and tappable bodies, wired to RealityKit's input. Pinch zooms the camera, drag orbits the viewpoint around the system, and a tap on a planet does the thing that makes it an app: selects it, eases the camera toward it, and surfaces an info panel (diameter, day length, moons, the one weird fact). That tap-to-focus camera move is the payoff, and the SwiftUI overlay carrying the info panel is where the screen design lives, the 3D scene below, a clean data card above. The same canvas-gesture craft underlies [the Apple Pencil drawing canvas](/blogs/apple-pencil-drawing-canvas-ui-react-native/), where direct manipulation has to feel physical.

Respect the platform: a play/pause for the orbital motion (constant animation is battery and a distraction when reading), Reduce Motion slowing or stopping the spin, and a reset-view button because users always tumble the camera into a confusing angle. A free [VP0](https://vp0.com) design supplies the surrounding chrome, the info cards, the body selector, the controls, so an agent builds the RealityView scene under a UI that was actually designed.

## Key takeaways: a solar system viewer in SwiftUI

- **RealityView with RealityKit is the modern path**; Model3D for a single USDZ, SceneKit only for legacy.
- **Orbits are parent-pivot rotations**, not per-frame trigonometry; moons nest under planets the same way.
- **Texture sells it**: NASA albedo maps, an emissive Sun, Saturn's rings as a textured alpha disc.
- **Pick the scale cheat deliberately and label it**: nothing watchable is fully to scale.
- **Tap-to-focus plus an info panel** is what turns a screensaver into an app; add play/pause and reset-view.

## Frequently asked questions

**How do I build an interactive 3D solar system viewer in SwiftUI?** Use RealityView with RealityKit: a parent Sun entity, each planet a child whose orbit is the rotation of an invisible pivot, textured with NASA albedo maps and an emissive Sun. Add pinch-zoom, drag-to-orbit, and tap-to-focus gestures with an info panel overlay. A free VP0 design supplies the surrounding cards and controls to build the scene under.

**Should I use RealityKit or SceneKit for a 3D solar system?** RealityKit via RealityView for new builds: it is Apple's current framework, renders physically-based materials, and works on iOS and visionOS. SceneKit still functions and has more legacy tutorials but is the aging path; reserve it for maintaining existing SceneKit code.

**How do I animate planet orbits without complex math?** Make each orbit the rotation of an invisible pivot entity centered on the Sun, with the planet positioned at its orbit radius as a child. Rotating the pivot circles the planet; rotating the planet entity spins it on its axis. No per-frame trigonometry, and moons nest under planets identically.

**Can a solar system app show real scale?** Not watchably: true relative sizes and distances make planets invisible specks in an empty void. Every usable viewer compresses scale, so choose the cheat (visually balanced, size-accurate, or fully accurate) deliberately and label it in the UI rather than implying false fidelity.

**How do I make the 3D scene interactive?** Wire RealityKit gestures: pinch to zoom the camera, drag to orbit the viewpoint, and tap a planet to select it, ease the camera toward it, and show an info panel. Add a SwiftUI overlay for the data cards and controls, plus play/pause and reset-view for usability.

## Frequently asked questions

### How do I build an interactive 3D solar system viewer in SwiftUI?

Use RealityView with RealityKit: a parent Sun entity, each planet a child whose orbit is an invisible pivot's rotation, textured with NASA albedo maps and an emissive Sun. Add pinch-zoom, drag-to-orbit, and tap-to-focus gestures with an info-panel overlay. A free VP0 design supplies the surrounding cards and controls.

### Should I use RealityKit or SceneKit for a 3D solar system?

RealityKit via RealityView for new builds: it is Apple's current framework, renders physically-based materials, and works on iOS and visionOS. SceneKit still functions with more legacy tutorials but is the aging path, best reserved for maintaining existing SceneKit code.

### How do I animate planet orbits without complex math?

Make each orbit the rotation of an invisible pivot entity centered on the Sun, with the planet positioned at its orbit radius as a child. Rotating the pivot circles the planet and rotating the planet entity spins it on its axis, with no per-frame trigonometry; moons nest under planets identically.

### Can a solar system app show the real scale?

Not watchably: true relative sizes and distances render planets as invisible specks in an empty void. Every usable viewer compresses scale, so choose the cheat (visually balanced, size-accurate, or fully accurate) deliberately and label it in the UI instead of implying false fidelity.

### How do I make the 3D solar system interactive?

Wire RealityKit gestures: pinch to zoom the camera, drag to orbit the viewpoint, and tap a planet to select it, ease the camera toward it, and show an info panel. Add a SwiftUI overlay for data cards and controls plus play/pause and reset-view for usability.

---
*Published on the [VP0 Journal](https://vp0.com/blogs). Free to read, index and cite with attribution.*
