Your House in 3D: From Phone Photos to a Printable STL
How I used Claude Vision to derive geometry from ordinary phone photos, CSG-meshed a watertight solid with manifold3d and trimesh, and validated it for 3D printing — all without touching a scan rig or a CAD file.
People love a physical model of their home. Until recently, getting one meant hiring a CAD modeler, renting a scan rig, or paying bespoke work. Your House in 3D solves this with a self-serve pipeline: upload a handful of phone photos, pay through Stripe, receive a watertight STL ready for the slicer.
The case study summarizes what the app does. This post is the engineering deep-dive into the three hardest parts: geometry extraction from photos, watertight mesh generation, and print-readiness validation.
The problem with scanning
The obvious approach is photogrammetry — reconstruct a point cloud from overlapping photos and remesh it. Tools like RealityCapture and Meshroom do this well. The problem is that they require controlled conditions: good lighting, >70% overlap between frames, and ideally a turntable or fixed camera rig. A homeowner with five casual snapshots on an iPhone does not meet these requirements.
The point cloud route also produces messy, non-manifold geometry that needs significant cleanup before a slicer will accept it. I wanted a pipeline that starts from a clean parametric representation, not a noisy reconstruction.
Phase 1: Multi-pass elevation analysis with Claude Vision
Instead of reconstructing geometry from correspondences, I asked Claude Vision to read the geometry directly from the images.
The prompt chain runs in three passes:
- Footprint pass — Claude identifies the plan shape of the building from the most overhead-like view available. It outputs a JSON polygon with relative coordinates (e.g.,
[{x: 0, y: 0}, {x: 1, y: 0}, ...]) normalized to a unit square. - Elevation pass — For each visible facade, Claude estimates the wall height relative to width, identifies window openings, and notes the roof pitch and ridge direction.
- Synthesis pass — Claude reconciles the footprint with the elevation data and produces a unified geometry spec: wall heights in real-world meters (inferred from door heights as a reference scale), roof ridge position and height, and a list of rectangular window cutouts with positions and dimensions.
Each pass uses a structured output schema so the FastAPI sidecar can parse the response deterministically. Passes are retried with a temperature increase if the output fails schema validation.
The key insight is that Claude is not doing photogrammetry — it is doing what an architect would do when asked to sketch a house from photos: reading proportions, using known references (doors, cars, people) to infer scale, and making reasonable assumptions about symmetry.
Phase 2: CSG meshing with manifold3d and trimesh
The geometry spec from Phase 1 feeds a Python pipeline that builds a solid using Constructive Solid Geometry:
import manifold3d as m3d
import trimesh
def build_house_solid(spec: GeometrySpec) -> trimesh.Trimesh:
# 1. Extrude the footprint polygon to wall height
walls = extrude_polygon(spec.footprint, spec.wall_height)
# 2. Build the roof as a prism over the ridge line
roof = build_roof_prism(spec.footprint, spec.ridge, spec.roof_height)
# 3. Boolean union: walls ∪ roof
solid = m3d.Manifold.from_trimesh(walls) + m3d.Manifold.from_trimesh(roof)
# 4. Subtract window and door cutouts
for opening in spec.openings:
box = build_opening_box(opening)
solid -= m3d.Manifold.from_trimesh(box)
return solid.to_trimesh()
manifold3d is the key dependency here. It implements the Manifold library (originally from Google) in Python, and guarantees that every boolean operation produces a manifold (watertight, orientable) result. Traditional mesh boolean libraries like pycsg or blender's Python API are notoriously unreliable on degenerate inputs; manifold3d handles them cleanly.
trimesh handles the I/O layer — exporting to STL and running geometric analysis — while manifold3d handles the boolean algebra.
Phase 3: Print-readiness validation
A watertight mesh is necessary but not sufficient for printing. I run four checks before accepting a model:
- Watertightness —
trimesh.is_watertight()confirms no open edges. - Volume sanity — the computed volume must fall within a plausible range for a residential house model at the requested scale (typically 1:200). Models that come out impossibly small or large indicate a geometry extraction failure and trigger a retry.
- Wall thickness — the slicer will silently ignore geometry thinner than the nozzle diameter (~0.4 mm at 1:200). I ray-cast from the interior outward to check that all walls meet a minimum thickness.
- Flat base — the model must have a flat, level bottom face so it sits on the print bed without supports. If the base is not flat, I translate and rotate the mesh until it is.
Only a model that passes all four checks is accepted and stored in S3 for the customer to download.
The storefront layer
The Next.js 16 storefront handles order flow: Google Maps address autocomplete collects the address (but the geometry comes entirely from the uploaded photos, not map data), Stripe Checkout processes payment, and a Route Handler polls the FastAPI sidecar for job status and streams progress back to the client.
The FastAPI sidecar runs on AWS Lambda with a 15-minute timeout (the full pipeline, including multiple Claude Vision passes, takes 2–8 minutes depending on the number of photos). Lambda's 10 GB memory limit is sufficient for the in-memory mesh operations.
What's next
The biggest limitation right now is multi-building scenes — Claude struggles when multiple structures are visible and tends to merge them into a single footprint. The next version will add a "crop to subject" preprocessing step that segments the primary building before feeding it to the Vision model.
Visit your-house-in-3d.idea-engine.studio to try it, or read the case study for the high-level overview.