Transform 2D floor plans into photorealistic 3D architectural renders using AI.
FloorPlan3D is a full-stack web application that uses AI to convert 2D architectural floor plans into photorealistic top-down 3D renders. Users upload a floor plan image and receive a professional visualization with realistic materials, furniture, and lighting — delivered in real-time via Server-Sent Events.
Florplan3d.mp4
sequenceDiagram
actor User
participant Client
participant API
participant DB
User->>Client: Login / Register
Client->>API: POST /auth/login
API->>DB: Verify credentials
DB-->>API: User found ✅
API-->>Client: Access Token (body) + Refresh Token (HttpOnly cookie)
Client->>Client: Store access token in Zustand
Note over Client,API: Later — access token expires
Client->>API: Request (expired token)
API-->>Client: 401 Unauthorized
Client->>API: POST /auth/refresh (cookie sent automatically)
API-->>Client: New access token
Client->>API: Retry original request ✅
sequenceDiagram
actor User
participant Client
participant API
participant Cloudinary
participant Redis
participant Worker
participant AI
User->>Client: Upload floor plan
Client->>API: POST /api/projects (image + form data)
API->>Cloudinary: Save original image
API->>DB: Create project record
API->>Redis: Queue render job
API-->>Client: 201 Created ✅ (instant response)
Note over Redis,Worker: Background — runs independently
Worker->>Redis: Pick up job
Worker->>AI: Generate render
AI-->>Worker: Rendered image
Worker->>Cloudinary: Upload render
Worker->>DB: Update project imageUrl
Worker-->>Client: 🔔 SSE event: project_updated
Client->>Client: Auto-refresh UI ✅
Two AI rendering providers are supported:
- ComfyUI — runs locally on your GPU using FLUX.2-klein. Free, no API limits.
- Gemini AI — Google's cloud-based image generation. No GPU required.
- Upload 2D floor plan images (JPG, PNG, WebP, SVG)
- AI-powered photorealistic render generation
- Choose between ComfyUI (local GPU) or Gemini AI (cloud)
- Real-time render status via Server-Sent Events (no polling!)
- Persistent background jobs via BullMQ + Redis (crash-safe)
- Private and community project visibility
- Delete project with Cloudinary image cleanup
- Google OAuth sign-in
- Email verification via Resend
- JWT authentication with automatic token refresh
- Rate limiting on all API endpoints
- User profile management with avatar upload
- Community gallery of shared projects
- Fully responsive UI
| Technology | Purpose |
|---|---|
| React Router v7 | SPA framework with file-based routing |
| TypeScript | Type safety |
| Tailwind CSS v4 | Styling |
| TanStack Query | Server state management & caching |
| Zustand | Client state management (auth tokens) |
| Axios | HTTP client with interceptors & auto-refresh |
| Sonner | Toast notifications |
| Zod | Client environment variable validation |
| Technology | Purpose |
|---|---|
| Node.js + Express | REST API server |
| TypeScript | Type safety |
| Prisma ORM v7 | Database access |
| PostgreSQL | Primary database |
| Redis | BullMQ job queue backing store |
| BullMQ | Persistent background job queue |
| JWT | Authentication (access + refresh tokens) |
| bcryptjs | Password hashing |
| Multer | File upload handling |
| Cloudinary | Image storage & CDN |
| Resend | Transactional emails |
| google-auth-library | Google OAuth token verification |
| Zod | Request & environment variable validation |
| express-rate-limit | API rate limiting |
| Technology | Purpose |
|---|---|
| ComfyUI | Local GPU-based rendering (FLUX.2-klein) |
| Google Gemini API | Cloud-based AI image generation |
| FLUX.2-klein-base-4b-fp8 | Diffusion model |
| qwen_3_4b | CLIP text encoder |
| Technology | Purpose |
|---|---|
| Docker + Docker Compose | Containerized development & production |
| Redis (docker) | BullMQ job queue |
| Cloudinary | Image CDN and storage |
| GitHub Actions | CI/CD pipeline |
| Technology | Purpose |
|---|---|
| Vitest | Unit & integration testing |
| React Testing Library | Component testing |
| ESLint | Code linting |
| @typescript-eslint | TypeScript linting |
FloorPlan3D/
├── client/ # React Router 7 frontend
│ ├── app/
│ │ ├── components/ # Reusable UI components
│ │ │ ├── Navbar/
│ │ │ ├── Profile/
│ │ │ ├── project/
│ │ │ │ ├── ProjectCard.tsx
│ │ │ │ └── ProjectCardSkeleton.tsx
│ │ │ ├── ui/
│ │ │ │ ├── ConfirmModal.tsx
│ │ │ │ ├── FullPageLoader.tsx
│ │ │ │ └── ...
│ │ │ └── visualizer/
│ │ │ ├── ComparisonSlider.tsx
│ │ │ └── VisualizerActions.tsx
│ │ ├── hooks/
│ │ │ ├── useAuth.ts
│ │ │ ├── useProject.ts # Includes useProjectUpdates (SSE)
│ │ │ └── useUser.ts
│ │ ├── routes/
│ │ │ ├── home.tsx
│ │ │ ├── my-projects.tsx
│ │ │ ├── visualizer.tsx
│ │ │ └── ...
│ │ ├── store/
│ │ │ └── authStore.ts # Zustand auth store
│ │ └── tests/ # Test files
│ │ ├── components/ # Component tests
│ │ ├── hooks/ # Hook tests
│ │ ├── store/ # Store tests
│ │ └── setup.tsx # Test setup
│ ├── lib/
│ │ │ └── axios.ts # Axios instance + interceptors
│ │ ├── env.ts # Zod client env validation
│ │ └── root.tsx
│ └── package.json
│
├── server/ # Express + TypeScript backend
│ ├── src/
│ │ ├── config/
│ │ │ ├── db.ts # Prisma client
│ │ │ └── env.ts # Zod server env validation
│ │ ├── controllers/
│ │ │ ├── auth.controller.ts
│ │ │ ├── project.controller.ts
│ │ │ └── user.controller.ts
│ │ ├── jobs/
│ │ │ ├── queue.ts # BullMQ queue + Redis connection
│ │ │ └── project.processor.ts # BullMQ Worker (AI rendering)
│ │ ├── middlewares/
│ │ │ ├── auth.middleware.ts
│ │ │ ├── authorize.middleware.ts
│ │ │ ├── rateLimit.middleware.ts
│ │ │ ├── validate.middleware.ts
│ │ │ └── upload.middleware.ts
│ │ ├── routes/
│ │ │ ├── auth.routes.ts
│ │ │ ├── project.routes.ts
│ │ │ └── user.routes.ts
│ │ ├── services/
│ │ │ ├── auth.service.ts
│ │ │ ├── project.service.ts
│ │ │ ├── gemini.service.ts
│ │ │ └── comfyui.service.ts
│ │ └── utils/
│ │ ├── ApiError.ts
│ │ ├── ApiResponse.ts
│ │ ├── cloudinary.ts # Upload + delete helpers
│ │ └── sse.ts # SSE manager
│ │ └── tests/ # Test files
│ │ ├── unit/ # Unit tests
│ │ ├── integration/ # Integration tests
│ │ └── setup.ts # Test setup
│ ├── prisma/
│ │ └── schema.prisma
│ └── package.json
│
├── docs/
│ └── architecture.png # Architecture diagram
├── .github/
│ └── workflows/
│ └── test.yml # CI/CD pipeline
├── docker-compose.yml
├── docker-compose.dev.yml
└── .env.example
- Node.js 20+
- Docker + Docker Compose
- NVIDIA GPU with 8GB+ VRAM (for ComfyUI)
- ComfyUI installed locally (for local rendering)
git clone https://github.com/Rai-Anish/FloorPlan3D.git
cd FloorPlan3Dcp .env.example .envOpen .env and fill in all required values (see Environment Variables).
Download the following models into your ComfyUI installation:
| Model | Path |
|---|---|
flux-2-klein-base-4b-fp8.safetensors |
models/diffusion_models/ |
qwen_3_4b.safetensors |
models/text_encoders/ |
flux2-vae.safetensors |
models/vae/ |
Start ComfyUI:
python main.py --listen 0.0.0.0 --port 8188docker-compose -f docker-compose.dev.yml up -dThis starts PostgreSQL, Redis, the Express server, and the Vite client.
docker-compose -f docker-compose.dev.yml exec server npx prisma migrate dev| Service | URL |
|---|---|
| Client | http://localhost:5173 |
| Server API | http://localhost:5000 |
| Prisma Studio | http://localhost:5555 |
| ComfyUI | http://localhost:8188 |
# Client tests
cd client && npm test
# Server tests
cd server && npm test
# Run tests once (CI mode)
cd client && npm run test:run
cd server && npm run test:run# Client lint
cd client && npm run lint
# Server lint
cd server && npm run lint
# Auto-fix linting issues
cd client && npm run lint:fix
cd server && npm run lint:fix# Generate coverage report
cd client && npm run test:coverage
cd server && npm run test:coverageGitHub Actions runs on every push and PR:
- Lint jobs — ESLint checks for client and server
- Test jobs — Vitest unit & integration tests
- Coverage jobs — Generate coverage reports
Create a .env file in the root directory. See .env.example for all variables.
# Database
POSTGRES_USER=FloorPlan3D
POSTGRES_PASSWORD=password
POSTGRES_DB=FloorPlan3D_db
DATABASE_URL=postgresql://FloorPlan3D:password@db:5432/FloorPlan3D_db
# Redis (auto-configured via Docker)
REDIS_URL=redis://redis:6379
# JWT
JWT_ACCESS_SECRET=
JWT_REFRESH_SECRET=
JWT_ACCESS_EXPIRES_IN=15m
JWT_REFRESH_EXPIRES_IN=7d
# Email (Resend)
RESEND_API_KEY=
FROM_EMAIL=onboarding@resend.dev
# Client
CLIENT_URL=http://localhost:5173
VITE_SERVER_URL=http://localhost:5000/api
VITE_GOOGLE_CLIENT_ID=
# Cloudinary
CLOUDINARY_CLOUD_NAME=
CLOUDINARY_API_KEY=
CLOUDINARY_API_SECRET=
# Google OAuth
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GOOGLE_CALLBACK_URL=http://localhost:5000/api/auth/google/callback
# AI Providers
GEMINI_API_KEY=
COMFYUI_URL=http://host.docker.internal:8188| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST |
/api/auth/register |
— | Register new user |
POST |
/api/auth/login |
— | Login |
GET |
/api/auth/verify-email |
— | Verify email token |
POST |
/api/auth/resend-verification |
— | Resend verification email |
POST |
/api/auth/refresh |
Cookie | Refresh access token |
POST |
/api/auth/logout |
— | Logout |
GET |
/api/auth/me |
Bearer | Get current user |
GET |
/api/auth/google |
— | Google OAuth redirect |
GET |
/api/auth/google/callback |
— | Google OAuth callback |
POST |
/api/auth/google/token |
— | Google GSI token verify |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/projects/community |
— | Get community projects |
GET |
/api/projects/my |
Bearer | Get my projects |
GET |
/api/projects/stream |
Bearer | SSE stream for live render updates |
GET |
/api/projects/:id |
Optional | Get project by ID |
POST |
/api/projects |
Bearer | Create project & queue render |
PUT |
/api/projects/:id |
Bearer | Update project (owner only) |
DELETE |
/api/projects/:id |
Bearer | Delete project + Cloudinary cleanup |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/user/me |
Bearer | Get profile + stats |
PUT |
/api/user/me |
Bearer | Update profile + avatar |
PUT |
/api/user/me/password |
Bearer | Change password |
DELETE |
/api/user/me |
Bearer | Delete account |
User uploads floor plan
↓
Express saves original image to Cloudinary
↓
Creates project record (imageUrl: "")
↓
Adds job to BullMQ queue → returns 201 immediately ✅
↓
BullMQ Worker picks up job (background)
↓
┌─────────────────────┐ ┌────────────────────────┐
│ ComfyUI Provider │ OR │ Gemini AI Provider │
│ │ │ │
│ 1. Upload to ComfyUI│ │ 1. Convert to base64 │
│ 2. Queue workflow │ │ 2. Send to Gemini API │
│ 3. Wait for result │ │ 3. Extract image │
└─────────────────────┘ └────────────────────────┘
↓
Upload rendered image to Cloudinary
↓
Update project.imageUrl in database
↓
Push SSE event → "project_updated" → client
↓
React Query invalidates cache → UI auto-refreshes ✅
| Layer | Implementation |
|---|---|
| Rate Limiting | Global: 100 req/15 min · Auth routes: 10 req/hour |
| Authentication | JWT access token (15m) + HttpOnly refresh cookie (7d) |
| Authorization | Ownership check on all project mutations (IDOR protection) |
| Environment Validation | Zod schema validation on server startup and Vite client boot |
| Password Hashing | bcryptjs |
| Input Validation | Zod schemas on all API request bodies |
The project uses the FLUX.2-klein image edit workflow which takes a reference image (the floor plan) and generates a photorealistic architectural render.
Workflow nodes used:
UNETLoader— loads FLUX.2-klein diffusion modelCLIPLoader— loads Qwen 3 4B text encoderVAELoader— loads FLUX2 VAEVAEEncode→ReferenceLatent— encodes floor plan as referenceCLIPTextEncode— encodes architectural promptCFGGuider+SamplerCustomAdvanced— sampling pipelineVAEDecode→SaveImage— decodes and saves output
Recommended GPU: RTX 3060 or higher (8GB+ VRAM)
View ComfyUI Workflow Details
[!NOTE] You can find the ComfyUI workflow JSON at:
docs/workflow.json.To use it:
- Open ComfyUI.
- Drag and drop the
.jsonfile from the/docsfolder directly into the browser.
# start all services (postgres, redis, server, client) with hot reload
docker-compose -f docker-compose.dev.yml up -d
# view logs
docker-compose -f docker-compose.dev.yml logs -f server
# run migrations
docker-compose -f docker-compose.dev.yml exec server npx prisma migrate dev --name your_migration
# open prisma studio
docker-compose -f docker-compose.dev.yml exec server npx prisma studiodocker-compose up -dThis project is licensed under the MIT License. See LICENSE for details.
- ComfyUI — local AI inference
- FLUX.2-klein — diffusion model by Black Forest Labs
- Google Gemini — cloud AI image generation
- BullMQ — Redis-based job queue
- Prisma — next-generation ORM
- TanStack Query — powerful data fetching
Built by Anish Rai
