Skip to content

Commit 7ccdaee

Browse files
committed
Readme; support drag n drop
1 parent c82ee0f commit 7ccdaee

File tree

2 files changed

+51
-7
lines changed

2 files changed

+51
-7
lines changed

README.md

+21-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,21 @@
1-
# spatial-photo-webxr-viewer
2-
View Apple's Spatial Photos in 3D VR with your browser via WebXR.
1+
# Apple Spatial Photo WebXR Viewer
2+
3+
[**Try it here.**](https://zachfox.github.io/spatial-photo-webxr-viewer) Spatial photos are processed locally and never leave your device.
4+
5+
With the launch of the iPhone 16 Pro, more phones than ever before are now capable of capturing 3D photographs. Apple calls these "Spatial Photos".
6+
7+
This small Web application allows you to view these spatial photos in 3D VR using WebXR.
8+
9+
## Why?
10+
11+
Spatial photography and spatial audio capture are untapped tools for human connection and recollection. This is mainly because experiencing spatial computing is still cumbersome and socially isolating, requiring use of expensive, uncomfortable headsets.
12+
13+
But those limitations are slowly disappearing, and XR technology is more accessible than it's ever been. As more people experience *meaningful* spatial content, more people are going to want to be able to capture and view that content.
14+
15+
I don't think Apple Spatial Photos should only be viewable on Apple devices. So, I built this viewer!
16+
17+
## Multidimensional Memories
18+
19+
Apple's hardware isn't yet capable of producing the truly immersive spatial content. For that, the photo's field of view must be wider, and the photos must be paired with spatial audio.
20+
21+
For the past few years, I have been experimenting with a new media format I'm calling "Multidimensional Memories". MDMs are wide-angle (180°) photographs paired with 30-second spatial audio clips. If Apple's Spatial Photos and Videos interest you, [**try out my Multidimensional Memories via WebXR here**](https://zachfox.photography/portfolio/immersive/).

src/components/Viewer.jsx

+30-5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ cameraLayers.enable(1);
1717
export default function Viewer() {
1818
const [imageSrc, setImageSrc] = useState("");
1919
const [isInXR, setIsInXR] = useState(false);
20+
const [isDraggingOver, setIsDraggingOver] = useState(false);
2021
const cameraRef = useRef();
2122

2223
const processBrowsedFile = (e) => {
@@ -53,8 +54,27 @@ export default function Viewer() {
5354
}
5455
}
5556

57+
const handleDrop = (e) => {
58+
e.preventDefault();
59+
60+
setIsDraggingOver(false);
61+
62+
if (!(e.dataTransfer.files && e.dataTransfer.files.length)) return;
63+
64+
setImageSrc(URL.createObjectURL(e.dataTransfer.files[0]));
65+
}
66+
67+
const handleDragOver = (e) => {
68+
e.preventDefault();
69+
setIsDraggingOver(true);
70+
};
71+
72+
const handleDragLeave = (e) => {
73+
setIsDraggingOver(false);
74+
};
75+
5676
return (
57-
<div className='w-full h-full min-h-screen bg-black absolute flex items-center'>
77+
<div className='w-full h-full min-h-screen bg-black absolute flex items-center' onDragOver={handleDragOver} onDrop={handleDrop} onDragLeave={handleDragLeave}>
5878
<Canvas
5979
gl={{ toneMapping: THREE.LinearToneMapping }}>
6080
<PerspectiveCamera
@@ -70,14 +90,19 @@ export default function Viewer() {
7090
</XR>
7191
</Canvas>
7292

73-
<div className='fixed left-1/2 -translate-x-1/2 bottom-0 bg-white/80 flex justify-center gap-4 p-4 rounded-t-md'>
93+
<div className='fixed left-1/2 -translate-x-1/2 bottom-0 bg-white/80 flex justify-center gap-4 md:gap-12 p-4 rounded-t-md'>
7494
<div className='flex flex-col gap-1'>
75-
<p className='font-semibold'>Select an Apple Spatial Photo <span className='font-mono'>(.heic)</span></p>
76-
<input type="file" accept=".heic" onChange={processBrowsedFile} />
95+
<p className='font-semibold'>Drop or select an Apple Spatial Photo <span className='font-mono'>(.heic)</span></p>
96+
<input className='cursor-pointer' type="file" accept=".heic" onChange={processBrowsedFile} />
7797
<p className='text-xs italic'>Your photo is processed on your device.</p>
7898
</div>
7999

80-
<button className='bg-amber-600 border-0 hover:border-2 active:border-4 focus:border-2 border-solid text-white px-8 py-1 font-semibold text-xl rounded-md transition-all duration-[25ms]' onClick={onXRButtonClicked}>{isInXR ? 'Exit' : 'Enter'} VR</button>
100+
<button className='bg-amber-600 outline-double outline-0 hover:outline-2 active:outline-4 focus:outline-2 outline-white text-white px-8 py-2 font-semibold text-xl rounded-md transition-all duration-[25ms]' onClick={onXRButtonClicked}>{isInXR ? 'Exit' : 'Enter'} VR</button>
101+
</div>
102+
103+
<div className={`absolute inset-0 rounded-md border-dashed border-2 border-white bg-neutral-800/90 text-amber-500 font-semibold text-xl md:text-2xl flex-col gap-2 justify-center items-center ${isDraggingOver ? "flex" : "hidden"} pointer-events-none`}>
104+
<p className='pointer-events-none select-none'>Drop your Apple Spatial Photo</p>
105+
<p className='font-mono text-sm pointer-events-none select-none'>.heic</p>
81106
</div>
82107
</div>
83108
)

0 commit comments

Comments
 (0)