Hands-on with virtual reality using A-Frame, React and Redux
Update March 19, 2017: I’m still seeing a lot of traffic to this article, so I’ve updated the example code on Github with the newest versions of all dependencies.
Having established why WebVR matters, it’s time to get our hands dirty and build a simple WebVR example using A-Frame, React and Redux. One of the things we can do with WebVR that a native VR app can’t do, is switching seamlessly between a normal html/css-based rendering of our state in the browser, and a 3d rendering of the same state. So let’s try that out with some running code.
I have created a small demonstration application based on the A-Frame React boilerplate project with Redux and Immutable added for keeping the state. The application uses the Meetup API to get the participants in the December meetup of the Copenhagen VR+AR+360 group and renders a list of their profile photos. When a photo is clicked, it changes tint color to indicate that the member has been classified as either a “hacker”, “hipster” or “hustler”.
By clicking a button, the user can change the view to a 3d rendering of the same list of members. If viewed on a VR-compatible device, the user will be placed inside a sphere of these profile photos with the ability to change the classification of each member using a gaze-controlled cursor to click on them. When looking down, there’s a red sphere that can be clicked to switch back to “2d mode”.
The demonstration application can be viewed live here — a mobile phone or WebVR-compatible browser is required to switch from 3d-mode to full stereoscopic VR-mode. The source code for the application is available on Github.
Let’s get technical
The application is fully built with React, Redux and Webpack — libraries that will be familiar to most front end developers. The aframe-react library acts as a bridge between A-Frame and React, and the react-redux library helps us connect our components cleanly with our state.
The state consists of two entries:
- A boolean vrMode that indicates if components should render their 3d/VR representation using A-Frame, or their 2d representation using regular html and css.
- An array of members with their id, name, photo and type.
When a member is clicked in either mode, the NEXT_MEMBER_TYPE Redux action is dispatched, the member type is changed and React takes care of re-rendering as needed. Because A-Frame is just markup and components from aframe-react are used, this re-rendering works seamlessly in both 2d and 3d mode.
When the button to enable 3d mode is clicked, the ENABLE_VR_MODE Redux action is dispatched. This simply changes the vrMode boolean in the state to true, and again Redux and React handles applying and rendering the new state.
In a large-scale application, it might be better to split components into “2d” and “3d” components and have containers choose the right one depending on the state, but in this example I have opted to have both renderings in the same component. The Wrapper container, as illustrated above, simply renders A-Frame markup when vrMode is enabled and regular html when it is not.
The Members container needs to only make one difference depending on the mode of rendering: React requires render methods to always return one node, and while this container node can be a regular div in 2d mode, it must be an A-Frame Entity element in 3d mode. The Entity element is the basic building block of A-Frame, but it is also A-Frame’s equivalent of a “neutral” container such as the div element. If a div is used as a container in 3d mode, nothing will be rendered in the canvas as the scene contains invalid markup.
The Member component (not to be confused with the plural Members container, sorry for the confusing naming), also looks at vrMode to determine how to render itself. In 3d mode, the Entity component provided by aframe-react is used to render a box with the profile photo as a texture tinted with the color derived from the member type. In 2d mode, a regular div with an image inside is rendered, and a state-modifying class is added depending on the member’s type.
In both cases, it is possible to use the same onClick callback when the member is clicked, whether that’s coming from clicking a div element on a web page or box in virtual reality. Redux handles the action and updates the state, and React re-renders as needed. In this setup, the libraries don’t need to know anything about how the state is rendered to do their thing.
Again, to review the source code in its entirety and play with it locally, head over to the repository on Github.
With A-Frame, web developers can easily get started on building 3d and VR visualizations, and they can apply these visualizations to existing React-based applications with a minimum of changes. This is especially true if the architecture uses the principles of Flux, as the shared unidirectional state and event flow lends itself well to many different representations of the same data.
I encourage everyone who are building VR business software to consider if a combination between 2d and 3d/VR might be the best approach, and if such a user experience wouldn’t make most sense in the browser. On top of that, it could also be faster to implement this way, depending on your team’s skill set.
Adding that “VR feature” to your existing web-based SaaS solution could be a low-hanging fruit with this approach. Wouldn’t your users prefer virtual reality visualizations directly in their browser as opposed to downloading yet another mobile app or — gasp — installing actual desktop software? Wouldn’t you?