← All posts

How I Built VibeHood: A 3D Neighbourhood Explorer for Singapore

The technical and design decisions behind building a MapLibre 3D map product as a solo builder — from a personal frustration to a shipped product.

The Problem

When I was hunting for a place to rent in Singapore, I did what most people do: I looked at the room, checked the price, and considered the nearby amenities I could think of. The apartment I chose was beautiful. The neighbourhood looked great on a map.

What I didn't think about was walkability — how far the nearest MRT actually was on foot, whether there was a hawker centre close enough to grab breakfast before work, whether basic errands would cost me 20 minutes each way. I ended up in a neighbourhood that looked good but quietly made daily life harder than it needed to be.

VibeHood was built from that experience.

What It Is

VibeHood is a 3D interactive map of Singapore's residential buildings. Every HDB block and private development is rendered at street level and coloured by how well it matches your lifestyle priorities — what I call a Vibe Score.

The score is personalised. A young family weights schools and childcare heavily. A young professional might care more about food and transport. You pick a profile, and the map responds — buildings light up or fade based on what actually matters to you, not a generic liveability index.

Layered on top is real transaction data: HDB resale prices, private sale PSF, and rental rates sourced from Singapore government open data. You can filter by budget, flat type, and transaction volume, then hover any building to see a breakdown by flat type — median price, recent transaction count, and 24-month trends.

How It Works

The map is built on MapLibre GL JS with 3D building extrusions. Building data is served as PMTiles — a single-file vector tile format hosted on Cloudflare R2 — which means the entire Singapore building dataset streams without a tile server.

The Vibe Score is computed using a weighted haversine walk from each building to nearby amenities: MRT stations, hawker centres, schools, parks, and clinics. Weights are adjustable per lifestyle profile and baked into tile properties at pipeline time, keeping the client fast even across 10,000+ buildings.

Transaction data is matched to buildings from HDB and URA open datasets, joined during a data pipeline build step and bundled as GeoJSON for fast in-browser filtering.

The Technical Decisions Worth Writing Down

Why PMTiles instead of a tile server?

Running a tile server for Singapore's entire building footprint would mean paying for a persistent backend just to serve a static dataset that rarely changes. PMTiles lets me host a single binary file on Cloudflare R2 (object storage) and serve range requests directly from the CDN. The entire tile set for Singapore buildings is under 50MB — fast, cheap, and zero server maintenance.

Why MapLibre over Mapbox?

Mapbox GL JS changed its licence in 2021 to a proprietary model. MapLibre is the open-source fork that maintains API compatibility — so it's essentially the same rendering engine with community-driven development and no token cost. For a personal product, this was an easy call.

State at the intersection of React and WebGL

The hardest part wasn't the mapping — it was managing state across a React UI and a WebGL canvas. MapLibre's rendering pipeline has subtle timing constraints: feature-state updates don't commit to WebGL before paint expressions evaluate. This meant seemingly simple colour changes (buildings updating as you drag a slider) required careful sequencing of setFilter calls as a flush mechanism — something that isn't in the MapLibre docs and took real debugging to figure out.

The data pipeline

Reconciling address formats across government datasets (HDB resale data, URA private transactions, OneMap amenity data) is genuinely messy. Building names are inconsistently formatted across sources. Planning boundary quirks from Singapore's historical rezoning mean some blocks don't match cleanly to their postal codes. The pipeline runs in Python, normalises addresses, joins across datasets, and outputs GeoJSON and tile properties that get baked into the PMTiles binary at build time.

What I Learned About Building a Product Solo

This was the first product I built entirely on my own from idea to shipped URL. A few things that surprised me:

Design decisions are the expensive ones. The coding was straightforward by comparison. The hard questions were: What does a "Vibe Score" actually mean? How many lifestyle profiles before the UI becomes confusing? What's the right level of data density before the map becomes noise? These decisions took longer than the implementation.

Ship before it's ready. I launched VibeHood before the rental data was fully working, before mobile was polished, before the comparison feature existed. The feedback I got in the first week shaped the roadmap more than anything I could have planned in advance.

The last 10% is a product, not a feature. Getting the map to feel right — the pan response, the hover timing, the colour transitions — is what separates something that works from something people want to use. That last 10% takes more time than the first 90%.

What's Next

VibeHood is live at vibehood.app and actively developed. Planned additions include expanded rental coverage, a neighbourhood comparison mode, and deeper private property analytics for a future Pro tier.

If you're building something that needs a map — whether it's a property product, a research tool, or a data explorer — let's talk.

Have thoughts? Find me on LinkedIn or get in touch.

← Back to all posts