Creating a Google Street View clone using react-photo-sphere-viewer

Nikhil Adiga
6 min readMay 24, 2024

--

Have you ever wondered how you would create a Google Street View clone?

A catchy image for the article that shows the logos of the technologies used.

Google Street View is a technology featured in Google Maps and Google Earth that provides interactive panoramas from positions along many streets worldwide. Google Street View displays interactive panoramas of stitched VR photographs.

This feature has always fascinated me. Even though a lot of manual work is required in acquiring the panorama photos, the way they have achieved this is incredible. I cannot even begin to fathom the complexity behind the scenes to scale such a huge feature while ensuring flawless user experience.

This made me wonder how I could create a clone, at least on a small scale of this amazing feature. So I got down to research.

Finally, I came across a library that would allow me to do this without much effort. The react-photo-sphere-viewer library does exactly what I wanted to achieve. It's a powerful library built on top of three.js. It has a lot of plug-ins that gives way to more advanced features.

This is a list of libraries/ plug-ins that we will be using in this project

  1. Vite
  2. React
  3. react-photo-sphere-viewer
  4. Virtual tour plugin for photo-sphere-viewer

Seems pretty simple, right? Let's go.

Create your react project using vite. I've been using Vite a lot nowadays, whenever I want to create a new React project. The transition from using the CRA template to Vite was super smooth. Vite uses native ES modules and modern browser APIs to compile your code on the fly, providing fast build times and instant updates in the browser. This leads to super fast hot module reloading and builds.

npm create vite@latest

This command will take you through a bunch of questions that ask you whether you want to go ahead with Vanilla JS, React, or some other popular frameworks. For this project, choose React and Javascript. Once you have set up the project, install this particular dependency.

npm i react-photo-sphere-viewer

You can now render 360-degree panoramic images in your project, but this is not enough to create the front end for a Google Street View type of application. You need to be able to stitch the images together, or at least make the user feel like the images are stitched 😉. To achieve this, you need to install the virtual tour plugin provided by the good folks who have created the original photo-sphere-viewer library. Run the following command to install the plug-in in your project.

npm i @photo-sphere-viewer/virtual-tour-plugin

Let's jump into the actual code now! 🤨

The ReactPhotoSphereViewer component takes in a lot of props which can be found in the official documentation or the npm website. The props which we are most interested in are these :

  1. src (The URL of the panorama image or the image object)
  2. height and width (height and width taken by the component to display the images)
  3. plugins (different plug-ins that are applied to the component)
  4. onReady (the callback function that gets triggered when the component instance is ready)

Let's see how each prop is handled in the code. 🧑‍💻

//Necessary imports
import React, { useRef } from "react";
import { ReactPhotoSphereViewer } from "react-photo-sphere-viewer";
import { VirtualTourPlugin } from "@photo-sphere-viewer/virtual-tour-plugin";
import "@photo-sphere-viewer/virtual-tour-plugin/index.css";

//Image files
import firstPhoto from "./assets/1.png";
import secondPhoto from "./assets/2.png";
import thirdPhoto from "./assets/3.png";

function App() {

//We need the instance of the component to apply plugins
const instanceRef = useRef(null);

//All plugins (can declare multiple)
const plugins = [[VirtualTourPlugin, { renderMode: "3d" }]];

//Function that instantiates plugin and data to it
const handleReady = (instance) => {
instanceRef.current = instance;

const virtualTour = instanceRef.current.getPlugin(VirtualTourPlugin);
virtualTour.setNodes([
{
id: "1",
panorama: firstPhoto,
name: "One",
links: [{ nodeId: "2", position: { textureX: 100, textureY: 1800 } }],
defaultZoomLvl: 0,
},
{
id: "2",
panorama: secondPhoto,
name: "Two",
links: [
{ nodeId: "1", position: { textureX: 3500, textureY: 1800 } },
{ nodeId: "3", position: { textureX: 100, textureY: 1800 } },
],
defaultZoomLvl: 0,
},
{
id: "3",
panorama: thirdPhoto,
name: "Three",
links: [{ nodeId: "2", position: { textureX: 3500, textureY: 1800 } }],
defaultZoomLvl: 0,
},
]);
};

return (
<>
<ReactPhotoSphereViewer
src={firstPhoto}
plugins={plugins}
height={"100vh"}
width={"100vw"}
onReady={handleReady}
></ReactPhotoSphereViewer>
</>
);
}

export default App;

Overall, the code is fairly self-explanatory. The key part of this code lies in the handleReady function. In this method, we are doing three things

  1. Instantiating the instance ref to the instance of our ReactPhotoSphereViewer component.
  2. Initializing the virtual tour plugin
  3. Initializing data for the plug-in.

Do not worry if you don't understand the image data initialization process. I'll try to explain the structure of the object with a simple pictorial representation.

The link between the image nodes

Imagine you're walking on a road and you take three images of the road as you walk forward. How do you tell which image comes after another? We as humans can understand the order of the images by looking at them manually, but how will you make your code understand this? For this reason, the virtual tour plug-in has the concept of nodes. They have a specific object structure that we need to follow to tell our code, which image should come after another i.e. which images are linked to each other. in this particular example, you can see that there are three images of the same road but in different positions. According to the logic, the first image is connected to the second image, the second image is connected to the first image as well as the third image, whereas the third image is connected to the second image.

Note that there can be more than two connections for an image.

{
id: "2", //image id , has to be unique
panorama: secondPhoto, //url or image object
name: "Two", //name of the image, will be visible in direction arrow toast
links: [ //connection to images
{ nodeId: "1", position: { textureX: 3500, textureY: 1800 } },
{ nodeId: "3", position: { textureX: 100, textureY: 1800 } },
],
defaultZoomLvl: 0,
}

Each image will have its own unique identifier (id) through which they can be linked to each other.

The panorama field indicates the photo object or the URL of the image to be displayed in this particular node.

The links object will contain an array of objects, which denote the connections to other nodes based on their ID and the location of the direction arrows.

Phew! 😅, I hope this made it clear. Running this code yields the following output.

Output

There you have it. We have just created the front end for a Google Street view type of application. Of course, I have not covered the back end in this article, but some of the ideas that could be implemented similar to Google Street view would be these

  1. Tiling the images so that they load faster (GDAL)
  2. Adding markers to known locations (Markers plugin)
  3. Maintaining connection data between image nodes in a database (MongoDB)
  4. Loading images from a cloud service (S3).

The possibilities are limitless. 📸

For example, if you're planning to sell a house, you could capture multiple 360-deg images using a camera like Insta360, stitch them together using node logic, deploy to a website and make it available to your clients so that they can get a virtual tour of your property.

The source code is available in my Github
https://github.com/Nikhiladiga/google-street-view-clone

Thanks for reading!

Follow to receive similar content in the future!

Knowledge is power

--

--

Nikhil Adiga
Nikhil Adiga

No responses yet