Case study

Listing Studio: a website for one house

A one-property website builder for realtors: a guided form, a live preview, and a public page with photos, a 3D walk-through, a daylight study, a map and a payment estimate. The demo listing is fictional.

ClientUlric studio product
Year2026
ScopeProduct design, Full-stack development, ASP.NET Core, Angular, three.js, Leaflet
Codegithub.com/erichers/ulric-listing-studio ↗
Listing Studio: a website for one house
43D views: solid, X-ray, cutaway, wireframe
3page themes
14-daylead chart on the dashboard
22passing xUnit tests

Why I built it

Some of my studio work is for realtors. I have built whole sites for agents and a licensed listing platform. This tool is for something smaller than a site: one house that needs its own page. The address as the headline, good photos, the facts, a map, a rough monthly payment, and a way to ask a question or book a showing. In my experience, agents often get that as a template inside a portal, with the portal's branding around their listing.

I wanted to see how small a tool for that could be. I also wanted something I could show a realtor in a meeting without a login screen in the way.

I made one decision before anything else. The demo uses no real listing. I did not want to copy photos or text from listing sites or the MLS, because both are covered by copyright and terms of use. So the demo is one fictional listing in Eugene, Oregon: a 1940s bungalow with a back deck and a finished basement. I blur the street address in every screenshot. The agent is made up too.

What it does

The builder is a multi-step form: address, price, beds, baths, square feet, lot, year built, description, neighborhood, schools, parks, features, photos, open houses, showing windows and agent details. A live preview sits beside the form, and there are three page themes to pick from, called Alder, Hearth and Linen.

Publishing creates a public page at /p/<slug>. It has a photo gallery with a lightbox, key facts, a 3D model of the house, a daylight study, a Leaflet map on OpenStreetMap tiles, neighborhood notes, and a mortgage calculator with a payment chart. When the API serves the built app, the page carries Open Graph tags, so a shared link shows the house.

The 3D model is built from the listing photos. It has four views: solid, X-ray with see-through walls and roof, cutaway with the roof and outside walls removed, and wireframe. Labels name each room. The model opens in full screen, and walk mode puts the camera inside the house. On a desktop you walk with WASD or the arrow keys. On a phone there is a joystick, and a menu jumps from room to room. On a phone the 3D view waits behind a still picture until you tap Open 3D view, so the page stays light.

In the last rounds the rooms got furniture, materials and light, so the walk-through reads close to a photo. The 3D still needs a real graphics card to look like that. A browser without one falls back to a plain wireframe.

The daylight study runs on the same model. The sun moves across the house for June 21, September 22 or December 21, and a play button runs the whole day.

A visitor can send a lead, which is stored for the dashboard. The notifier only writes a log line, so no email goes out. A visitor can also pick an open showing slot. The same slot cannot be booked twice, and a booked showing downloads as an .ics calendar file.

The dashboard shows each listing, leads with a status of new, contacted, showing or offer, an upcoming showings calendar, view and lead counts, and a small chart of leads over the last 14 days. Each listing also has a one-page PDF flyer.

There is no login. This is a local demo, and the README says so.

How it's built

The API is ASP.NET Core 8. The front end is Angular 22 with standalone components and signals. Data goes to SQLite through EF Core by default, or to MySQL through Pomelo. Leaflet and OpenStreetMap draw the map, Chart.js draws the payment breakdown, three.js draws the 3D house and the daylight study and loads only on a listing page, and QuestPDF writes the flyer.

Geocoding uses Nominatim, the free OpenStreetMap geocoder. Its usage policy asks for low volume and an honest user agent, so the API calls it from the server, one request at a time, at least 1.1 seconds apart, with a user agent that names the project. Results are cached by normalized address, so the same address is not looked up twice. The map pins in the demo are neighborhood stand-ins, not surveyed parcels, and the school notes tell the reader to confirm the boundary.

The data model is small. Everything hangs off one listing.

The payment section follows the order a buyer thinks in. Price minus the down payment gives the loan. The loan, rate and term give principal and interest. Tax, insurance and HOA go on top.

A few details exist because my Mac runs MAMP with MySQL 5.7, not 8. The migration uses utf8mb4_general_ci, because the newer MySQL 8 collations do not exist on 5.7. The geocode cache key is limited to 191 characters, so its index stays under the 767-byte limit on 5.7. The local connection string turns TLS off, because a .NET 8 process could not complete the handshake with the local 5.7 server.

The xUnit suite was at 22 passing tests at the last run. It covers the mortgage math, showing slot conflicts and slug generation.

What I learned

Fictional data is a decision you have to make on purpose, and early. It was tempting to use a real listing to make the demo look alive. Doing it properly meant writing a believable house and a fictional listing around it. It took longer, and it means I can show the demo to anyone.

The dashboard chart taught me something about SVG. On the live install, the date labels on the 14-day lead chart came out bigger than the stat numbers above them. The labels were SVG text inside a chart that scaled with its viewBox, so when the chart got wider, the text grew with it. The fix was a fixed-height chart of about 150 pixels, with 12-pixel labels and thin columns. It is a small thing. It is also the kind of thing a test suite never sees and a person notices in one second.

The 3D house is modelled from the photos, not from a survey of the lot, and the orientation of the sun path is a demo assumption. I kept the copy honest about both. While the 3D view loads, or when WebGL is not available, a still elevation drawing shows instead.

What's next

  • Agent accounts. Right now anyone who can reach the app can edit it, which is fine for a demo on my own machine and not fine anywhere else.
  • Starting a page from a licensed listing feed instead of a blank form. I already do IDX work for agents, and that is the natural source for real listings.
  • A real house, with an agent's permission, so the demo can sit next to a live example.

Tech stack

  • ASP.NET Core 8 Web API
  • Angular 22 with standalone components and signals
  • SQLite and EF Core by default, or MySQL through Pomelo
  • Leaflet with OpenStreetMap tiles
  • Nominatim geocoding, cached and rate limited on the server
  • Chart.js for the payment breakdown
  • three.js for the 3D house: solid, X-ray, cutaway, wireframe, and walk views, plus the daylight study
  • QuestPDF for a one-page flyer

Repository

The GitHub repository for this app is github.com/erichers/ulric-listing-studio.

Daylight study on desktop
Daylight study on desktop
Daylight study on desktop and phone
Daylight study on desktop and phone
3D interior view on desktop and phone
3D interior view on desktop and phone
Monthly payment calculator on desktop and phone
Monthly payment calculator on desktop and phone
Listing page on three phones
Listing page on three phones
Dark mode listing page
Dark mode listing page

Common questions

Is the demo listing a real house?

No. The demo listing is fictional, and so is the agent. The street address is blurred in every screenshot.

Does the lead form send email?

No. Leads are stored for the dashboard, and the notifier only writes a log line.

Can two visitors book the same showing slot?

No. A slot can be booked once, the check is covered by a test, and a booked showing downloads as an .ics calendar file.

← All work