- Demo a 3D SPA using your chosen 3D framework
- Ensure application is responsive and works on standard desktop browsers
While many 3D rendering libraries are currently available, my cursory research highlighted the following contenders:
- three.js
- Babylon.js, 24K stars on Github
- PlayCanvas
| Name | Pros | Cons | Github ⭐ |
|---|---|---|---|
| three.js | - Wide 3D rendering features - Mature React integration via React Three Fiber (29K⭐) - Collection of ready-made "plugins" via Drei (9K⭐) - Continuous historical growth |
- Complexity due to low-level graphics API - No professional support AFAICT |
107K⭐ |
| Babylon.js | - Wide 3D rendering features - 3D Editor - React-Babylonjs renderer (0.9K⭐ ) - Supported by Microsoft |
- Extraneous features due to being a game engine (E.g. physic engine) - Smaller community than threejs |
24K⭐ |
| PlayCanvas | - Wide 3D rendering features - Comprehensive 3D Editor & Asset tooling - Online integration via Web Component API or React renderer - Paid app hosting |
- Extraneous features due to being a game engine (E.g. physic engine) - Much smaller community |
10K⭐ |
All three engines support advanced rendering features like PBR texturing or transparency.
See also this Wikipedia WebGL frameworks comparison table for more info.
It's clear that three.js is the most mature & popular 3D WebGL library out there. It has a large active community providing lots of reusable code samples and, after 11 years of activity, shows no sign of slowing down.
On the other hand, Babylon.js and PlayCanvas are still very potent contenders thanks to their graphic feature completeness and e2e production tooling. But they seem to appeal to a smaller professional audience (e.g. game studios) as they are built & marketed as game engines; which is overkill for most general web applications.
For the purpose of this 3D demo app, I'll use three.js.
pnpm install
pnpm build --turbo
pnpm startOpen http://localhost:3000 with your browser to see the result.
It's recommended to use Docker to create a build & run this demo in a container:
# CWD: this git repo
scripts/docker/build_n_dev.sh# start local webserver
pnpm dev
# run test suite
pnpm test-
Models:- Typescript model definitions of the app schema.
Product/DraftProduct: physical product (e.g. a bottle of beer)ContainerTemplate: template of a product container. E.g. Generic bottleContainerMaterial: physical material category. E.g. plastic, glass, etc...ImageAsset: image assetModelAsset: 3D model asset
- Typescript model definitions of the app schema.
-
DataLayer:- Abstraction layer for the backend
- Provides API to retrieve models asynchronously
- E.g. get product draft, get product templates
-
ProductEditorPage: an async React Server Component page for Next.js.- Renders a layout with a main section containing the
ProductEditorcomponent. - Passes
containerMaterialIDandcontainerTemplateIDfrom URL query params toProductEditorto render initial product config.
- Renders a layout with a main section containing the
-
ProductEditor:- A React component responsible for rendering and managing the product editing UI.
- Expects an
initprop with initial values forcontainerMaterialIDandcontainerTemplateID. - Manages form state (
DraftProductfromModels.ts), basic validation, and render logic to display a 3D model for the product. - Displays a Share button to facilitate bookmarking the current page state.
-
ProductEditorCanvas:- React component dedicated to render a 3D product preview on a UI canvas
- Target 3D product asset are loaded asynchronously
- React Three Fiber canvas and Drei components faciliate the following:
- viewport resizing
- ThreeJS canvas/object unmounting
-
Lazy3DModels:- React component to help load 3D model assets asynchronously.
- Individual models can be loaded thanks to the
React.lazy()API
- This demo simulates backend connections via an async DataLayer API.
- Limited Jest unit testing since webGL canvas isn't available by default. Ideally, e2e tests would add better test coverage.
- Jest
--watchfeature doesn't detect file changes on Windows + Docker. Just trigger test reruns manually... - DataLayer is not fully used yet
- Default 3D scene & camera settings are still hard-coded in a React component
- When loading the product editor with the beer bottle model by default, the view is occluded because the camera position is inside the bottle.
- A "Loading..." notification is supposed to appear when the 3D model is being downloaded; but it doesn't always consistently appear
- Some React warnings appear during Jest unit tests (related to
act()) but I still manage to test the main target functionalities. (Seesrc/components/__tests__/ProductEditor.test.tsx)
- It was my first time using many of the tools used in this demo: next.js, Tailwind, Three.js & React Three Fiber
- It took some time setting these up and getting my head around their core APIs
- Getting a decent understanding of what causes React components to be rendered in SSR/clientside by next.js
- Challenges with Three.js:
- Getting a general understanding of the various parts of the three.js 3D modeling concepts (scene, camera, mesh objects)
- Figuring out how to import and load open-source 3D models in GLTF/GLB format
- Making a WIP loader indicator for Three.js; it's still unstable at this level