A visionOS app for Apple Vision Pro that transforms your 2D images into immersive 3D spatial photos. Browse your media library from a Stash server, local files, or use the built-in demo mode.

Spatial Stash links two shared packages:
| Package | Products used |
|---|---|
RAVESDK | RAVENet, RAVEUI, RAVEConsole, RAVEMedia |
RAVEEngine | RAVEDiagnostics |
Both are referenced as local Swift packages by relative path —
../../RAVESDK and ../../RAVEEngine, resolved against the directory holding
SpatialStash.xcodeproj — not as versioned remote dependencies. So a clone does
not fetch them: check them out as siblings of this repo, which is what step 1
below does.
The requirement is only that this repo's parent directory also contains
directories named exactly RAVESDK and RAVEEngine; this repo's own directory
name does not matter. Get it wrong and Xcode fails at package resolution, before
compiling anything.
Why path references and not versions: the packages and the apps co-evolve continuously — several of these targets arrived in the packages by being lifted out of this app — and a path reference keeps "move this into the package and update its callers" a single atomic edit.
Clone this repository and both packages into the same parent directory:
git clone https://github.com/illixion/spatialstash.git
git clone https://github.com/illixion/RAVESDK.git
git clone https://github.com/illixion/RAVEEngine.git
giving you:
some-parent/
├── RAVESDK/
├── RAVEEngine/
└── spatialstash/
Open the project in Xcode:
cd spatialstash
open SpatialStash/SpatialStash.xcodeproj
Select your development team in Xcode (Project → Signing & Capabilities)
Build and run on visionOS Simulator or device (Cmd+R)
The app starts in demo mode with sample images. No configuration required.
To connect to your Stash server:
http://192.168.1.100:9999)To use local files:
Browse your image gallery in a grid view. Tap any image to open it in the viewer:
Browse and play videos. Tap the view-mode button in the ornament to switch between flat 2D, Pseudo 3D (2D→3D conversion), and (for tagged SBS/OU sources) full immersive Stereoscopic 3D:
Converts any flat video into windowed stereoscopic 3D using Core ML monocular depth (Depth Anything V2):
scripts/convert-depth-model.pyVideos automatically detected from Stash tags (SBS/Over-Under format) are converted to MV-HEVC and play in full immersive mode.
Create complex queries to filter your media (Stash server only):
scripts/convert-depth-model.py) is imported automatically on launchA slideshow viewer for displaying images from a RoboFrame proxy API, with WebSocket-based remote control and Home Assistant sensor integration. Enable in Settings → Developer → Enable Remote API Viewer.
playVideo and showText WebSocket commands open new windows for home system notificationsSpatial Stash can play arbitrary web videos — including YouTube — and convert them to Pseudo 3D on the fly. This is what makes it easy to watch, say, a 4K YouTube video in windowed stereoscopic 3D on Vision Pro. Enable Web yt-dlp Support in Settings → Developer.
hvc1 so AVPlayer decodes it natively, and the proxy stream-copies already-HEVC sources rather than transcoding.spatialstash://play?url=… callback URL scheme. Any direct video link (MP4/HLS) opens and plays immediately without the proxy; web page links are routed through web-yt-dlp when it's enabled. The easiest way to hand a link over is the Open in Spatial Viewer Shortcut — add it, then use the Share sheet on any video or link to send it straight into Spatial Stash. A bookmarklet that opens the same URL scheme works too.The app follows a SwiftUI architecture with:
AppModel - Central @Observable state container for app-wide statePhotoWindowModel - Per-window @Observable model for photo viewers, split into extensions by concern (visual adjustments, spatial 3D, background removal, memory management, gallery navigation, UI controls)PhotoDisplayView + PhotoOrnamentView - Shared components used by all three photo viewer windowsImageSource, VideoSource) for swappable implementationsStashAPIClient actor for thread-safe GraphQL communicationImagePresentationComponent for spatial photosStereoscopicVideoPlayer + MVHEVCConverter for stereoscopic 3D video conversion and immersive playbackPseudo3DVideoPlayerView + StereoPump + CoreMLDepthProvider for real-time, windowed 2D→3D video conversion (per-eye Metal warp off the main thread)DepthConverter + DepthCacheStore + DepthCacheReader for offline depth conversion pipeline: per-frame monocular depth inference with joint-bilateral luma-guided filtering, temporal lookahead smoothing, and compact HEVC depth sidecar + metadata storageDepthConversionManager for background conversion job queue with progress tracking, progressive playback (auto-engage 3D mid-conversion when safe), and resumption after app backgroundingPseudo3DStereoEngine configurable for real-time or cached depth source, both at exact-frame PTS matching for zero ghostingSee LICENSE.txt for licensing information.
564 commits
Swift
98.2%
A visionOS app for Apple Vision Pro that transforms your 2D images into immersive 3D spatial photos. Browse your media library from a Stash server, local files, or use the built-in demo mode.

Spatial Stash links two shared packages:
| Package | Products used |
|---|---|
RAVESDK | RAVENet, RAVEUI, RAVEConsole, RAVEMedia |
RAVEEngine | RAVEDiagnostics |
Both are referenced as local Swift packages by relative path —
../../RAVESDK and ../../RAVEEngine, resolved against the directory holding
SpatialStash.xcodeproj — not as versioned remote dependencies. So a clone does
not fetch them: check them out as siblings of this repo, which is what step 1
below does.
The requirement is only that this repo's parent directory also contains
directories named exactly RAVESDK and RAVEEngine; this repo's own directory
name does not matter. Get it wrong and Xcode fails at package resolution, before
compiling anything.
Why path references and not versions: the packages and the apps co-evolve continuously — several of these targets arrived in the packages by being lifted out of this app — and a path reference keeps "move this into the package and update its callers" a single atomic edit.
Clone this repository and both packages into the same parent directory:
git clone https://github.com/illixion/spatialstash.git
git clone https://github.com/illixion/RAVESDK.git
git clone https://github.com/illixion/RAVEEngine.git
giving you:
some-parent/
├── RAVESDK/
├── RAVEEngine/
└── spatialstash/
Open the project in Xcode:
cd spatialstash
open SpatialStash/SpatialStash.xcodeproj
Select your development team in Xcode (Project → Signing & Capabilities)
Build and run on visionOS Simulator or device (Cmd+R)
The app starts in demo mode with sample images. No configuration required.
To connect to your Stash server:
http://192.168.1.100:9999)To use local files:
Browse your image gallery in a grid view. Tap any image to open it in the viewer:
Browse and play videos. Tap the view-mode button in the ornament to switch between flat 2D, Pseudo 3D (2D→3D conversion), and (for tagged SBS/OU sources) full immersive Stereoscopic 3D:
Converts any flat video into windowed stereoscopic 3D using Core ML monocular depth (Depth Anything V2):
scripts/convert-depth-model.pyVideos automatically detected from Stash tags (SBS/Over-Under format) are converted to MV-HEVC and play in full immersive mode.
Create complex queries to filter your media (Stash server only):
scripts/convert-depth-model.py) is imported automatically on launchA slideshow viewer for displaying images from a RoboFrame proxy API, with WebSocket-based remote control and Home Assistant sensor integration. Enable in Settings → Developer → Enable Remote API Viewer.
playVideo and showText WebSocket commands open new windows for home system notificationsSpatial Stash can play arbitrary web videos — including YouTube — and convert them to Pseudo 3D on the fly. This is what makes it easy to watch, say, a 4K YouTube video in windowed stereoscopic 3D on Vision Pro. Enable Web yt-dlp Support in Settings → Developer.
hvc1 so AVPlayer decodes it natively, and the proxy stream-copies already-HEVC sources rather than transcoding.spatialstash://play?url=… callback URL scheme. Any direct video link (MP4/HLS) opens and plays immediately without the proxy; web page links are routed through web-yt-dlp when it's enabled. The easiest way to hand a link over is the Open in Spatial Viewer Shortcut — add it, then use the Share sheet on any video or link to send it straight into Spatial Stash. A bookmarklet that opens the same URL scheme works too.The app follows a SwiftUI architecture with:
AppModel - Central @Observable state container for app-wide statePhotoWindowModel - Per-window @Observable model for photo viewers, split into extensions by concern (visual adjustments, spatial 3D, background removal, memory management, gallery navigation, UI controls)PhotoDisplayView + PhotoOrnamentView - Shared components used by all three photo viewer windowsImageSource, VideoSource) for swappable implementationsStashAPIClient actor for thread-safe GraphQL communicationImagePresentationComponent for spatial photosStereoscopicVideoPlayer + MVHEVCConverter for stereoscopic 3D video conversion and immersive playbackPseudo3DVideoPlayerView + StereoPump + CoreMLDepthProvider for real-time, windowed 2D→3D video conversion (per-eye Metal warp off the main thread)DepthConverter + DepthCacheStore + DepthCacheReader for offline depth conversion pipeline: per-frame monocular depth inference with joint-bilateral luma-guided filtering, temporal lookahead smoothing, and compact HEVC depth sidecar + metadata storageDepthConversionManager for background conversion job queue with progress tracking, progressive playback (auto-engage 3D mid-conversion when safe), and resumption after app backgroundingPseudo3DStereoEngine configurable for real-time or cached depth source, both at exact-frame PTS matching for zero ghostingSee LICENSE.txt for licensing information.
564 commits
Swift
98.2%