Mobile VR App Development for Virtual Tours
Virtual tour is a connected route through space: apartment, office, museum, hotel. User navigates between viewpoints (hotspots), looks around at each point, interacts with objects. Technically more complex than just 360 video: requires inter-scene navigation, interactive elements, and transitions without "teleport" feeling.
Tour Architecture: Graph of Scenes
Tour is a graph. Each point (node) is 360-panorama or 3D scene. Graph edges are transitions (hotspots). Data stored in JSON config:
{
"tour_id": "apartment_demo",
"start_node": "living_room",
"nodes": [
{
"id": "living_room",
"type": "equirectangular",
"media_url": "scenes/living_room_8k.jpg",
"hotspots": [
{ "id": "to_kitchen", "target_node": "kitchen",
"position": { "yaw": -45, "pitch": -10 },
"label": "Kitchen" },
{ "id": "info_tv", "type": "info",
"position": { "yaw": 20, "pitch": 5 },
"content": "Samsung QLED 65\"" }
]
}
]
}
Client loads graph on startup, preloads media of adjacent nodes.
Media Type: Photos vs 3D Scenes
Equirectangular photos — most common virtual tour format. Captured on Ricoh Theta, Insta360, or specialized DSLR rig. Photo quality determines realism factor. 8K JPEG = good result.
3D scenes (Unity/Godot, real-time rendering) — more flexible, interactive, but require more resources and content creation time. For architectural visualization — justified choice.
Google Street View-style — sequence of panoramas with smooth transitions. Between points short transition video plays, creating "walking" illusion.
Panorama Rendering: WebGL vs Native
Two main approaches:
WebView + Three.js / A-Frame — tour viewer runs in WebView. Iterates quickly, content updates on server without app release. Limitation: WebGL in WKWebView (iOS) and WebView (Android) has limited IMU access — head tracking works worse, latency higher.
Native rendering — Metal (iOS), Vulkan/OpenGL ES (Android), or Unity for cross-platform. Low latency, direct IMU access, full Cardboard VR mode. More complex to develop and update content.
For full VR in Cardboard — only native rendering. For non-VR viewing (no headset, just phone rotation) — WebView sufficient.
Transitions Between Points
Hard jump between 360 scenes creates discomfort. Smooth transition options:
- Fade to black — simplest, least VR-discomfort
- Fade + scale — scene "shrinks" on exit, "grows" on entry
- Video transition — short "movement" video between points (realistic, but requires additional filming)
// Unity: transition coroutine with fade
IEnumerator TransitionToScene(string targetNodeId) {
yield return StartCoroutine(FadeOut(duration: 0.5f));
LoadScene(targetNodeId);
yield return StartCoroutine(FadeIn(duration: 0.5f));
}
Teleportation in VR via fade — standard, recommended by Google VR Design Guidelines.
Interactive Hotspots
Hotspot in space = raycast from gaze center + gaze dwell activation. Hotspot types:
- Navigation — transition to another point
- Info panel — popup card with text/photo/video
- Media — video playback on surface (TV in interior)
- Link — browser transition for external action (book, buy)
Hotspot renders in world space on Billboard (always facing camera). Scale depends on distance — constant apparent size via transform.LookAt(camera) + scale = distance * constant.
CMS and Content Updates
Tours must update without app release. Graph config and media files stored on CDN, app loads on startup. For offline mode — cache selected tours with config version check.
Workflow
Content audit: media type (photos, video, 3D), scene count, update requirements.
Tour structure design: scene graph, hotspot types, navigation logic.
Renderer selection: WebView or native (with/without Cardboard VR).
Development: panorama rendering, head tracking, hotspot interaction, transitions.
CMS or config schema for content updates without release.
Test on devices, assess head tracking quality in Cardboard mode.
Timeline Estimates
Basic single-tour app with photo-panoramas and navigation hotspots — 2–3 weeks. Full platform with CMS, multiple hotspot types, offline mode, Cardboard VR — 2–3 months.







