Empowering furniture designers to create, visualise, and present room layouts — from a quick 2D top-down canvas to an immersive, orbit-able 3D scene.
🚀 Live Demo · 🎬 Video Walkthrough · ⚡ Quick Start · 🏗️ Architecture
📋 Table of Contents (click to expand)
FurnishView is a web-based room design tool built for in-store furniture consultations. A designer logs in, inputs a customer's room specifications, places furniture on a 2D top-down canvas, then generates a full 3D walkthrough-ready scene — all in the browser, with no installation required.
Built as coursework for PUSL3122 — HCI & Computer Graphics (2025–26), the project demonstrates layered MVC architecture, hand-coded 2D canvas algorithms, Three.js 3D rendering with proper lighting and shading, and a full user-centred design process backed by formative and summative usability studies.
| Attribute | Detail |
|---|---|
| Module | PUSL3122 — HCI & Computer Graphics |
| University | Plymouth University Sri Lanka (PUSL) |
| Year | 2025–26 |
| Tech | JavaScript (ES6+) · HTML5 Canvas · Three.js r160 · CSS3 |
| Run | Open index.html in Chrome/Firefox — no npm, no server, no setup |
|
🏠 Room Design
🖱️ 2D Canvas Engine
🌐 3D Visualisation
|
📐 Design Comparison (Innovation)
🤖 Auto-Arrange (Innovation)
🛒 E-commerce Pipeline (Innovation)
🔐 Role-Based Auth
💾 Data Handling
|
FurnishView enforces strict Model–View–Controller separation. Each layer has a defined responsibility — crossing boundaries is a code smell and is actively avoided.
flowchart TD
subgraph Models["📦 Models (js/models/)"]
R[Room.js]
F[FurnitureItem.js]
D[Design.js]
end
subgraph Views["🖼️ Views (js/views/)"]
LV[LoginView.js]
DV[DashboardView.js]
EV[EditorView.js]
subgraph Components["components/"]
TB[Toolbar.js]
SB[Sidebar.js]
M[Modal.js]
T[Toast.js]
end
end
subgraph Controllers["🎮 Controllers (js/controllers/)"]
DC[DesignController.js]
CC[CanvasController.js]
end
subgraph Graphics["🎨 Graphics (js/graphics/)"]
R2D[Renderer2D.js]
R3D[Renderer3D.js]
CM[ColourManager.js]
end
subgraph Utils["🛠️ Utils (js/utils/)"]
ST[storage.js]
SC[scaleCalculator.js]
CN[constants.js]
end
app.js -->|initialises| Views
Views -->|calls| Controllers
Controllers -->|mutates| Models
Controllers -->|triggers| Graphics
Graphics -->|reads| Models
Graphics -->|uses| Utils
Controllers -->|uses| Utils
Models -->|serialised by| ST
| Layer | Responsibility | What it never does |
|---|---|---|
| Models | Pure data — room/furniture/design state | No DOM, no rendering, no events |
| Views | Generate HTML, render to DOM, bind events | Never access models directly for logic |
| Controllers | Orchestrate views ↔ models, handle business logic | No direct DOM manipulation, no raw canvas calls |
| Graphics | All canvas and Three.js rendering | Knows nothing about the DOM outside its canvas |
- Zero build step — no Webpack, Vite, or npm. Open
index.htmland it works. Assessed work must run offline. - Three.js r160 bundled locally in
lib/— CDN failure cannot break submissions. MeshStandardMaterialenforced — noMeshBasicMaterialanywhere. All geometry responds to lighting for visible shading.preserveDrawingBuffer: trueon the WebGL renderer — enables PNG export without a blank canvas bug.
- A modern browser: Chrome 90+ or Firefox 88+ (WebGL and ES modules required)
- For ES module support without a server: use VS Code Live Server extension
# Clone the repository
git clone https://github.com/deaneeth/furnish-view-hci-project.git
cd furnish-view-hci-project
# Open in your browser
# On Windows:
start index.html
# On macOS:
open index.html
# On Linux:
xdg-open index.html
⚠️ Some browsers block ES module imports fromfile://URLs. If the app shows a blank screen, use Option 2.
- Open the project folder in VS Code
- Install the Live Server extension if not already installed
- Right-click
index.html→ Open with Live Server - App opens at
http://127.0.0.1:5500
# Python 3
python -m http.server 8080
# Then visit http://localhost:8080
# Node.js (npx, no install needed)
npx serve .
# Then visit http://localhost:3000| Role | Username | Password |
|---|---|---|
| Designer | designer1 |
password123 |
| Designer | designer2 |
password123 |
| Admin | admin |
admin123 |
FurnishView uses a client-side, role-based authentication system backed entirely by localStorage.
furnishview_users → Array of registered designer credentials
furnishview_session → Currently active session (cleared on logout)
furnishview_designs → All saved Design objects (filtered per designer on load)
| Role | Access |
|---|---|
| Designer | Create, view, edit, and delete their own designs only |
| Admin | View and manage all designs across all designers; system-level controls |
Session persists across browser refreshes until the user explicitly logs out. No cookies, no tokens, no network calls.
🖱️ 2D Canvas Engine (Renderer2D.js)
The 2D canvas is a hand-coded computer graphics engine built on the raw HTML5 Canvas API. No canvas frameworks are used — every algorithm is implemented from scratch.
Coordinate Scaling
scaleFactor = canvasWidth / roomWidthInMeters
pixelX = realWorldX × scaleFactor
pixelY = realWorldY × scaleFactor
All furniture positions are stored in real-world meters. On every render pass, they are scaled to canvas pixels using this factor.
Hit Detection A classic point-in-axis-aligned-rectangle (AABB) test for click/select:
isHit = (clickX >= item.x) && (clickX <= item.x + item.width)
&& (clickY >= item.y) && (clickY <= item.y + item.depth)
Items are tested in reverse render order so the topmost rendered item gets selection priority.
Boundary Clamping Furniture cannot be dragged outside the room boundary:
item.x = clamp(item.x, 0, room.width - item.width)
item.y = clamp(item.y, 0, room.depth - item.depth)
Drag Offset
On mousedown, the offset between the cursor and the item's top-left corner is captured:
offset.x = mouseX - item.x
offset.y = mouseY - item.y
During mousemove, position is updated as item.x = mouseX - offset.x — the item follows the cursor without jumping.
Render Loop
clearRect() → draw room outline → draw grid (optional) → draw all furniture → highlight selected item. Runs on every mouse event during drag via requestAnimationFrame.
🌐 3D Visualisation Engine (Renderer3D.js)
Built with Three.js r160. The scene is constructed entirely from primitives — no external 3D model files.
Scene Setup
// Core pipeline
Scene → PerspectiveCamera → WebGLRenderer (preserveDrawingBuffer: true) → OrbitControls
// Lighting (critical for shading marks)
AmbientLight(0xffffff, 0.6) // Base illumination
DirectionalLight(0xffffff, 0.8) // Creates shadows + visible shadingRoom Geometry
- Floor —
PlaneGeometry, rotated –90° on X axis, positioned at Y = 0 - Walls — Four thin
BoxGeometrymeshes positioned at room edges
Furniture Geometry
- Each item →
BoxGeometry(width, height, depth)withMeshStandardMaterial - Positioned by mapping 2D canvas coordinates → 3D world space:
3D.x = (2D.x / scaleFactor) - (roomWidth / 2) 3D.z = (2D.y / scaleFactor) - (roomDepth / 2) 3D.y = furnitureHeight / 2 // sits on the floor
Colour Synchronisation
ColourManager.js maps hex colour strings from the model to Three.js Color objects, keeping 2D and 3D appearances identical.
Export
const dataURL = renderer.domElement.toDataURL('image/png')
// Works because preserveDrawingBuffer: true was set on construction🤖 Auto-Arrange Algorithm (Innovation)
The auto-arrange feature distributes furniture items within the room using a rule-based spatial algorithm:
- Calculate the room's usable area (accounting for wall clearance — typically 0.5m from each wall)
- Group furniture by type (seating, tables, storage)
- Place tables first at functional positions (dining table centred, bedside tables flanking bed)
- Distribute seating around tables using angular offsets
- Fill remaining space with storage units along walls
- Run boundary clamp on all positions to guarantee no overlaps with walls
The algorithm respects standard interior design clearance rules (e.g., 0.9m aisle width around dining tables) derived from BS 8300 accessibility guidelines.
📐 Design Comparison Mode (Innovation)
Renders two saved designs simultaneously in a split-screen layout:
- Two independent Three.js
WebGLRendererinstances, each targeting a separate<canvas>element - Both share the same lighting parameters for fair visual comparison
OrbitControlsattached to each canvas independently — rotating one view does not affect the other- The designer selects two designs from the dashboard; the comparison view loads them in parallel
This reuses 100% of the existing Renderer3D class — the comparison view is essentially two editor panels with their sidebars hidden.
🛒 E-commerce Pipeline (Innovation)
After finalising a design, the designer can generate a customer take-home document:
- PNG Export —
canvas.toDataURL('image/png')for the 2D view;renderer.domElement.toDataURL()for 3D - PDF Export — Uses
jsPDF(loaded fromlib/) to compose a document containing:- Design name and date
- 2D layout screenshot
- 3D render screenshot
- Bill of materials — furniture items with type, colour, and catalogue dimensions
The generated PDF is downloaded directly in the browser — no server upload required.
🏠 Room Preset Templates (Innovation)
Defined in js/data/roomPresets.js, each preset auto-populates the room setup form:
| Preset | Width | Depth | Height | Default Wall | Default Floor |
|---|---|---|---|---|---|
| Studio Apartment | 6m | 5m | 2.8m | #F5F0EB |
#C8B89A |
| Living Room | 5m | 4m | 2.8m | #E8E0D4 |
#8B7355 |
| Dining Room | 4.5m | 3.5m | 2.6m | #EDE8E0 |
#6B5344 |
| Bedroom | 4m | 3.5m | 2.7m | #F0EBE5 |
#A0896C |
Selecting a preset fires a single event that populates all six form fields instantly, supporting Heuristic #7 — Flexibility and Efficiency of Use (Nielsen, 1994).
furnish-view-hci-project/
├── index.html # Entry point — loads js/app.js as ES module
│
├── css/
│ ├── main.css # Global styles, CSS custom properties, layout
│ ├── components.css # Toolbar, Sidebar, Modal, Toast styles
│ └── canvas.css # 2D/3D canvas container sizing
│
├── js/
│ ├── app.js # Initialisation, view routing (login → dashboard → editor)
│ │
│ ├── auth/
│ │ └── auth.js # Login validation, session management
│ │
│ ├── models/ # ── Pure data objects, zero DOM/rendering ──
│ │ ├── Room.js # Dimensions, shape, wall/floor colour
│ │ ├── FurnitureItem.js # Type, position, dimensions, colour, rotation
│ │ └── Design.js # One Room + FurnitureItem[] + metadata
│ │
│ ├── views/ # ── DOM rendering, HTML generation ──
│ │ ├── LoginView.js
│ │ ├── DashboardView.js
│ │ ├── EditorView.js
│ │ └── components/
│ │ ├── Toolbar.js # Furniture catalogue + action buttons
│ │ ├── Sidebar.js # Room & furniture property panels
│ │ ├── Modal.js # Reusable confirmation / wizard modals
│ │ └── Toast.js # Non-blocking action notifications
│ │
│ ├── controllers/ # ── Event handling, logic orchestration ──
│ │ ├── DesignController.js # CRUD design workflow
│ │ └── CanvasController.js # Mouse/touch events on 2D canvas
│ │
│ ├── graphics/ # ── All rendering logic ──
│ │ ├── Renderer2D.js # HTML5 Canvas 2D engine (hand-coded algorithms)
│ │ ├── Renderer3D.js # Three.js scene manager
│ │ └── ColourManager.js # Colour sync across 2D + 3D views
│ │
│ ├── utils/
│ │ ├── storage.js # localStorage CRUD wrapper
│ │ ├── scaleCalculator.js # Real-world ↔ pixel/unit conversions
│ │ └── constants.js # App-wide constants, default values
│ │
│ └── data/
│ ├── furnitureCatalogue.js # Furniture type definitions + default dimensions
│ └── roomPresets.js # Pre-built room templates (innovation feature)
│
├── lib/ # Three.js r160 — committed, works offline
│ ├── three.module.js
│ ├── three.min.js
│ └── OrbitControls.js
│
├── assets/
│ ├── screenshots/ # App screenshots for README and report
│ ├── textures/ # Optional floor/wall texture maps
│ └── sample-data/ # Pre-loaded designs for demo (video)
│
├── formative-docs/ # Formative usability study documentation
│ ├── P1.md / P2.md # Raw observer session notes
│ ├── participant-P1.md # Structured observation sheet — P1
│ ├── participant-P2.md # Structured observation sheet — P2
│ ├── observer-sheet.md # Study summary and findings
│ └── observation-sheet-RESEARCHER-USE.md # Filled researcher log
│
└── requirements/
├── PROJECT OVERVIEW.md # Full project blueprint and architecture spec
├── End-to-End-Project-Roadmap.md # Phase-by-phase implementation roadmap
└── REPO_WORKFLOW.md # Branch, commit, and PR conventions
FurnishView underwent a two-phase HCI evaluation following the think-aloud protocol and System Usability Scale (SUS) methodology.
| Attribute | Detail |
|---|---|
| When | After lo-fi prototypes, before hi-fi design |
| Method | Think-Aloud Protocol |
| Participants | P1, P2 (anonymised adults, target user profile) |
| Tasks | 5 realistic in-store consultation tasks |
| Artefacts | Test plan · Consent forms · Observation sheets · Session notes |
Key findings that shaped the hi-fi design:
- The "3D View" toggle was labelled "Visualise" in the lo-fi prototype — participants did not associate it with 3D. → Renamed to "3D View" in hi-fi.
- The undo button was missing from the lo-fi toolbar — P1 accidentally deleted furniture and could not recover. → Undo added as a persistent toolbar action.
- Toolbar icons were considered too small on a tablet-sized display. → Icons enlarged to 32 × 32px minimum in hi-fi.
- P2 expected clicking a furniture item to open its properties automatically. → Sidebar refreshes on item selection in implementation.
| Attribute | Detail |
|---|---|
| When | After working app was complete |
| Method | Think-Aloud Protocol + SUS Questionnaire |
| Participants | P3, P4 (anonymised adults) |
| SUS Score | 78.75 / 100 — "Good" usability band (Bangor et al., 2009) |
| Task Completion | 100% across all 5 tasks for both participants |
Key SUS interpretation: Score 78.75 places FurnishView in the "Good" tier (above the industry average of 68), indicating the interface is learnable, efficient, and satisfying for the target user demographic.
Areas for further improvement identified:
- 3D scene takes slightly longer to load for rooms with many items (optimisation opportunity)
- Rotation handle for furniture on the 2D canvas was not immediately discoverable (discoverability improvement needed)
| # | Heuristic | FurnishView Implementation |
|---|---|---|
| 1 | Visibility of System Status | Toast notifications on every action; selected furniture highlighted; loading indicator during 3D build |
| 2 | Match System and Real World | Dimensions in metres; furniture named "Dining Table" not "Item_003"; spatial layout mirrors a physical room |
| 3 | User Control and Freedom | Undo button for placement/removal; cancel on all dialogs; back navigation from editor to dashboard |
| 4 | Consistency and Standards | Single colour picker component reused everywhere; Ctrl+S saves in all views; consistent button hierarchy |
| 5 | Error Prevention | Confirmation modal before delete; room dimension validation (no negative/zero values); boundary clamping |
| 6 | Recognition Rather Than Recall | Furniture shown as visual thumbnails; saved designs as named cards; recent colours visible in picker |
| 7 | Flexibility and Efficiency of Use | Drag-and-drop; Del key removes selection; room presets reduce setup from 6 fields to 1 click |
| 8 | Aesthetic and Minimalist Design | Clean layout — only relevant controls visible per mode; whitespace used intentionally |
| 9 | Help Users Recover from Errors | Descriptive validation messages; undo for accidental changes; non-destructive colour edits |
| 10 | Help and Documentation | Tooltips on all toolbar buttons; placeholder text in all form fields |
| Principle | FurnishView Application |
|---|---|
| Visibility | Toolbar always visible; active tool highlighted; selected item shows handles |
| Feedback | Toast on every action; canvas redraws instantly on drag; 3D updates on colour change |
| Constraints | Boundary clamping; form validation; radio buttons for room shape (not free text) |
| Consistency | Same colour picker for walls, floor, and furniture; Ctrl+S always saves |
| Affordance | Furniture thumbnails invite dragging (cursor changes on hover); colour swatches look selectable |
This is academic coursework — external PRs are not expected, but team contributions are tracked via Git.
For team members:
- Branch from
dev— never commit directly tomaingit checkout dev git pull origin dev git checkout -b feature/your-feature-name
- Follow the commit convention from
requirements/REPO_WORKFLOW.md:feat(canvas): implement drag-and-drop furniture placement fix(3d): resolve shadow rendering on wall geometry docs(eval): add summative study observation notes - Open a PR to
devwith a description that references the relevant task from the roadmap - Get at least one review before merging
- Never merge broken code — verify
index.htmlopens without console errors first
Dineth Hettiarachchi Lead Developer Architecture · 2D Engine · 3D Scene · Innovation Features |
Lakindu Pahesara Design Lead Personas · Storyboards · Lo-Fi / Hi-Fi Prototypes · Report |
Thanuji Kalanika Evaluation Lead Test Plans · User Studies · SUS Scoring · Consent Forms |
This project is submitted as academic coursework for PUSL3122 at Plymouth University Sri Lanka. All original source code is available under the MIT License for reference purposes.
MIT License — Copyright (c) 2026 Dineth Hettiarachchi and contributors
| Library | Version | License | Source |
|---|---|---|---|
| Three.js | r160 | MIT | threejs.org |
| OrbitControls.js | r160 | MIT | three/examples |
| jsPDF | 2.x | MIT | parallax/jsPDF |
- Nielsen, J. (1994). 10 Usability Heuristics for User Interface Design. NN/g.
- Norman, D. (1988). The Design of Everyday Things. Basic Books.
- Preece, J., Sharp, H. & Rogers, Y. (2019). Interaction Design. 5th ed. Wiley.
- Brooke, J. (1996). SUS: A Quick and Dirty Usability Scale.
- Dirksen, J. (2018). Learn Three.js. 3rd ed. Packt.
- Angel, E. & Shreiner, D. (2014). Interactive Computer Graphics. 7th ed. Pearson.
- MDN Web Docs — Canvas API
- Three.js Documentation — threejs.org/docs






