A first-person, WASD-walkable 3D real-estate experience in the browser — walk through a home like a video game (mouse-look, jump, crouch, sprint, wall collision) — plus a live 3D Gaussian Splat showcase. No build step, no app, no VR headset. Just open the page and walk.
Live:
https://wilsonwu-ai.github.io/roam-3d/
Inspired by the viral "someone killed the real-estate industry with 3D Gaussian Splatting" thread pointing at PlayCanvas. Roam takes the honest, shippable version of that idea and makes it real today.
| Mode | What you get |
|---|---|
Walk (index.html) |
A data-driven, procedurally-built house you explore in first person across all three floors — walk the ramp staircases between the lower level, main floor, and upstairs. WASD move · mouse look · Space jump · Shift sprint · Ctrl/C crouch · wall collision · floor-aware minimap + badge · room labels · click a photo to inspect. |
Splat (splat.html) |
A real 3D Gaussian Splat rendered live in your browser — a sample scene, honestly labeled (see below). |
| Input | Action |
|---|---|
W A S D / Arrows |
Move |
Mouse |
Look (click to capture) |
Space |
Jump |
Shift (hold) |
Sprint |
Ctrl / C |
Crouch |
E / Click |
Inspect the photo you're facing |
M |
Toggle minimap |
Esc |
Pause / settings menu |
The viral thread conflates two different things:
- Scan a house with your phone → walk it — real, but needs a dense video capture (hundreds of overlapping frames), camera-pose estimation (COLMAP / glomap), and GPU training of a Gaussian-Splat model.
- Any Zillow listing becomes a photoreal splat — not real. You cannot reconstruct a splat from ~30 wide-angle marketing photos; they're sparse, non-overlapping, and unposed.
So Roam is deliberately split:
- The Walk experience is built from a listing's floor plan + its real room photos textured onto the walls — genuinely "walk the listing."
- The Splat mode loads a real public Gaussian splat as a technology showcase, clearly labeled "sample scene, not this listing." No dishonesty.
The real capture→splat pipeline (for when you do shoot a dense walkthrough) is
in tools/capture-listing.md.
Two decoupled engines behind one shell — which is what makes it robust on GitHub Pages:
index.html ──► Three.js r185 (walkable house) three@0.185.1 (pinned)
splat.html ──► @mkkellogg/gaussian-splats-3d 0.4.7 three@0.160.0 (pinned, isolated)
They never share a WebGL context or a three version, so the walkable house
(newest verified Three) and the splat viewer (which peer-depends on an older
Three) can't conflict.
listing.json is the single source of truth. Swapping in a real Zillow
listing is a data-and-images change only — zero code.
The world is multi-floor: each room declares a floor, floors have an
elevation, and stairs define walkable ramps between them. Stairwell cells use
openToAbove / openToBelow to leave a vertical shaft; the player walks the ramp
(a tilted collider with decorative treads) between levels. Cross-floor
connections keep the graph connected without cutting a horizontal doorway.
listing.json ──► buildWorld.js ──► per-floor walls (doorway gaps) + slabs + ceilings
├─ visible meshes (offset by floor elevation)
├─ Octree collision (walls + slabs + ramps)
└─ room zones w/ floor (HUD label + floor-aware minimap)
└► photoPanels.js ──► aspect-correct, unlit, SRGB photo panels (decor)
| Path | Purpose |
|---|---|
index.html |
App shell — pinned importmap, gate/HUD/pause DOM. |
main.js |
Bootstrap, state machine (gate→walk↔pause↔inspect), render loop. |
js/loadListing.js |
Fetch + validate (no room overlaps; BFS reachability). |
js/buildWorld.js |
Data→scene compiler (edge-merged walls with doorway subtraction). |
js/player.js |
Capsule-vs-Octree physics: gravity, jump, sprint, crouch (+ un-crouch ceiling probe). |
js/controls.js |
Pointer-lock mouse-look (sensitivity, invert-Y) + keyboard intent. |
js/hud.js |
Room label, facts, crosshair, live minimap with heading cone. |
js/ui.js |
Gate, pause/settings (localStorage), mobile gate, photo tour. |
js/splat.js |
mkkellogg splat viewer (reads listing.splat from listing.json). |
listing.json |
Floor plan + facts + photo map + splat config. |
css/styles.css |
Warm-neutral dark + brass premium visual system. |
.nojekyll |
Required so GitHub Pages serves all files. |
Physics mirror the canonical three.js games/fps
reference: Octree + Capsule, GRAVITY = 30, fixed sub-stepping
(Math.min(0.05, delta) / 5) to prevent tunneling.
python3 -m http.server 8123
# open http://127.0.0.1:8123/No build, no npm install. Three.js and the splat lib load from a pinned CDN
(jsDelivr) via <script type="importmap">.
- Follow
tools/capture-listing.mdto grab facts + room photos intoassets/rooms/(downscaled withsips -Z 1600). - Edit
listing.json: facts,heroImage, and each room'sphotos[].url. - Reload. The world rebuilds from data — no code change.
Rooms not yet photographed render a tasteful "photo pending" placeholder, so the app is always walkable.
Built with Claude Code (Opus 4.8, 1M context) in a single session, using an agentic pipeline:
Skills invoked
karpathy-guidelines— first-principles framing for the research phase.threejs— engine grounding.frontend-design— the premium landing gate + HUD visual direction.superpowers:using-superpowers— skill discipline.
Agent workflow (5 parallel agents via the Workflow orchestrator, Karpathy framing)
- Three.js FPS engine research — verified r185 APIs, CDN URLs, the
THREE.Timerbreaking change, collision/crouch/sprint code. - Gaussian-splat integration research — compared mkkellogg vs PlayCanvas
supersplat-viewervs engine gsplat; chose the isolated mkkellogg path; flagged the GitHub-PagesSharedArrayBuffergotcha. - UX / product spec — screen flow, HUD, settings, accessibility, mobile.
- Zillow→floorplan data model — the
listing.jsonschema + a validated example floor plan. - Architecture synthesis — reconciled the four into this build plan.
QA — driven headlessly with Playwright: collision (sprint into a wall → stops at the capsule radius), locomotion, doorway passage, and the splat load were all verified before deploy.
PlayCanvas / Gaussian-Splatting toolchain (forked on this account, the same
stack the viral thread points at, kept for the real capture→splat roadmap):
engine ·
supersplat ·
supersplat-viewer ·
splat-transform ·
video-scout.
Runtime dependencies (CDN, pinned)
three@0.185.1— walkable house.@mkkellogg/gaussian-splats-3d@0.4.7+three@0.160.0— splat mode.- Fonts: Fraunces + Inter (Google Fonts, with system fallbacks).
- Sample splat:
train.splat(public).
- Swap in a real captured Zillow listing (data + photos).
- Walk inside a splat — mkkellogg
DropInViewer+ the WASD rig on an isolated page. - Touch controls (virtual joystick + drag-look) for a walkable mobile mode.
- End-to-end real capture→splat of a home (video-scout → COLMAP → 3DGS training → splat-transform).
Personal, non-commercial proof-of-concept. Listing photos, when added, remain © their listing agent / MLS — attributed on-screen and linked to source. Not affiliated with Zillow or PlayCanvas.