AR‑Visualizer is a browser‑native augmented reality workspace that lets users upload 3D assets, place them in real‑world space using WebXR, and save persistent scenes to the cloud. It is designed as a cinematic, production‑grade reference application showcasing modern full‑stack patterns:
- Frontend: React 18 + TypeScript + Vite, with React Three Fiber for 3D and
@react-three/xrfor WebXR AR sessions. - Backend: Express (MVC) on Node 20+ with Helmet, CORS, rate limiting, JWT authentication, and Supabase as the data + storage layer.
- Database: PostgreSQL on Supabase with full Row‑Level Security policies, plus a
ar-assetsstorage bucket for uploads.
The UI takes design cues from Apple Vision Pro, Linear, Arc Browser, Nothing OS, Tesla UI, and Adobe Substance 3D — clean type, glass surfaces, and motion that feels precise rather than decorative.
| Area | Capability |
|---|---|
| Auth | Email/password sign‑up & sign‑in, JWT sessions, protected routes, forgot‑password flow |
| Upload Studio | Drag‑and‑drop GLB/GLTF/USDZ uploads, automatic asset processing pipeline, thumbnail generation |
| AR Workspace | WebXR session entry, hit‑test placement, object selection/translation, gesture‑based controls |
| Scene Persistence | Save & reload positioned objects per user, cloud‑synced via Supabase |
| Asset Library | Personal library with filtering, preview, and deletion |
| Dashboard | Activity log, upload counts, recent scenes |
| Security | Helmet, CORS allow‑list, rate limiting, bcrypt password hashing, RLS at the database level |
| DX | Type‑safe API client, Zustand state stores, React Hook Form + Zod validation |
- React 18, TypeScript, Vite 5
- Tailwind CSS 3 with a custom design token system
- React Router 6
- React Three Fiber 8 + Drei +
@react-three/xr6 - Zustand 5 for global state
- React Hook Form + Zod
- Axios with an interceptor layer
- Node.js 20+ (ESM)
- Express 4 (controllers / services / routes split)
- Helmet, CORS,
express-rate-limit, Morgan - bcryptjs + jsonwebtoken
- Multer (memory storage) + Sharp for image processing
@supabase/supabase-js(service‑role client)
- PostgreSQL via Supabase
- Supabase Storage (
ar-assetsbucket) - Row‑Level Security on every table
┌──────────────────────┐
│ Browser │
│ React + R3F + XR │
└─────────┬────────────┘
│ HTTPS (JWT)
▼
┌──────────────────────┐
│ Express API │
│ (controllers / │
│ services / mw) │
└─────────┬────────────┘
│ service‑role key
▼
┌──────────────────────┐
│ Supabase │
│ Postgres + Storage │
│ + RLS policies │
└──────────────────────┘
The browser talks only to the Express API. The API holds the Supabase service‑role key and is the single source of truth for writes, ensuring RLS cannot be bypassed from the client.
AR-Visualizer/
├── client/
│ ├── public/
│ └── src/
│ ├── components/ # Navbar, ProtectedRoute, HeroVisual, Logo3D
│ ├── layouts/ # AppLayout, AuthLayout
│ ├── pages/ # Landing, Login, Register, Dashboard, UploadStudio, ARWorkspace, Library, ForgotPassword
│ ├── services/ # api, auth, asset, scene
│ ├── store/ # authStore, sceneStore
│ ├── xr/ # PlacedObject and XR helpers
│ └── utils/
├── server/
│ ├── config/ # env, supabase
│ ├── controllers/ # auth, asset, scene
│ ├── services/ # auth, asset, scene
│ ├── routes/ # auth, asset, scene
│ ├── middleware/ # auth, error, upload, validate
│ ├── xr-processing/ # asset pipeline
│ ├── utils/ # ApiError, asyncHandler
│ ├── app.js
│ └── server.js
└── database/
└── schema.sql # Tables + RLS + storage policies
- Node.js 20+ and npm 10+
- A free Supabase project (URL + service role key + anon/publishable key)
- A modern browser with WebXR support for AR (Chrome on Android, or a WebXR‑capable headset)
Open the Supabase SQL editor and run the full contents of database/schema.sql. This creates all tables, RLS policies, and the ar-assets storage bucket.
cd server
cp .env.example .env # then edit with your real values
npm install
npm run dev # http://localhost:5000cd client
cp .env.example .env # then edit with your real values
npm install
npm run dev # http://localhost:5173Open http://localhost:5173, create an account, and you're in.
| Key | Description |
|---|---|
PORT |
API port (default 5000) |
CLIENT_ORIGIN |
Allowed CORS origin (e.g. http://localhost:5173) |
JWT_SECRET |
Long random string used to sign session tokens |
JWT_EXPIRES_IN |
Token lifetime (e.g. 7d) |
SUPABASE_URL |
https://<project>.supabase.co |
SUPABASE_SERVICE_ROLE_KEY |
Supabase secret key (server‑only) |
SUPABASE_STORAGE_BUCKET |
Storage bucket name (default ar-assets) |
| Key | Description |
|---|---|
VITE_API_URL |
Backend base URL incl. /api, e.g. http://localhost:5000/api |
VITE_SUPABASE_URL |
Public Supabase project URL |
VITE_SUPABASE_ANON_KEY |
Publishable (anon) key |
Both
.envfiles are git‑ignored. Never commit real keys.
Defined fully in database/schema.sql:
- users — accounts (email, name, avatar, hashed password)
- uploaded_assets — 3D files in Supabase Storage with metadata
- ar_sessions — per‑user AR session telemetry
- saved_scenes — JSON scene graphs of placed objects
- activity_logs — audit trail
- subscriptions — billing tier per user
All tables have Row‑Level Security enabled. The backend uses the service‑role key to perform validated writes on behalf of the authenticated user.
Base URL: <server>/api
| Method | Endpoint | Description |
|---|---|---|
POST |
/auth/register |
Create account |
POST |
/auth/login |
Email + password login → JWT |
POST |
/auth/forgot-password |
Request password reset |
GET |
/auth/me |
Current user (requires Authorization: Bearer <jwt>) |
| Method | Endpoint | Description |
|---|---|---|
POST |
/assets/upload |
Upload a 3D file (multipart) |
GET |
/assets |
List my assets |
GET |
/assets/:id |
Get one asset |
DELETE |
/assets/:id |
Delete asset + storage object |
| Method | Endpoint | Description |
|---|---|---|
POST |
/scenes |
Save scene graph |
GET |
/scenes |
List my scenes |
GET |
/scenes/:id |
Load scene |
DELETE |
/scenes/:id |
Delete scene |
| Script | Purpose |
|---|---|
npm run dev |
Start Vite dev server on :5173 |
npm run build |
Type‑check + production build → dist/ |
npm run preview |
Preview the production build locally |
npm run lint |
ESLint over src/ |
| Script | Purpose |
|---|---|
npm run dev |
Start Express with --watch |
npm start |
Start Express in production mode |
- Push this repo to GitHub.
- On vercel.com → Add New… → Project → import the repo.
- Configure Project:
- Root Directory:
client - Framework Preset: Vite
- Build Command:
npm run build - Output Directory:
dist
- Root Directory:
- Environment Variables:
VITE_API_URL→ your deployed backend, e.g.https://ar-visualizer-api.onrender.com/apiVITE_SUPABASE_URLVITE_SUPABASE_ANON_KEY
- Deploy.
The Express server is a long‑running process (rate limiter + multer + Sharp), so a traditional Node host fits best.
Render:
- New → Web Service → connect repo.
- Root Directory:
server - Build Command:
npm install - Start Command:
node server.js - Add all server env vars from the table above.
- Set
CLIENT_ORIGINto your final Vercel URL.
Just keep the same project used in development, or create a new one for production and re‑run database/schema.sql.
- Multiplayer shared scenes (Supabase Realtime)
- USDZ Quick Look fallback on iOS Safari
- Asset versioning + soft delete
- Public scene gallery
- Stripe‑backed subscription tiers
- CLI for bulk asset upload
PRs and issues are welcome. Please:
- Fork the repo and create a feature branch.
- Run
npm run lintinclient/and ensure the server starts cleanly. - Open a pull request describing the change and any screenshots/screen recordings.
MIT © Premkumar — built with care.