Bentuk Kota
An atlas measuring 33 Indonesian neighbourhoods twice, by car and on foot, to show what car-only metrics miss

- sites, each measured by car and on foot
- 33
- sites flagged for thin footway coverage
- 9
- rejected candidates published with their reason
- 22
- network calls at runtime
- 0
On this page
The problem
People who study city street patterns mostly use one measure: orientation entropy, from Geoff Boeing's 2019 paper. It is computed on the driving network. In Indonesia that misses most of how people actually move. A kampung is threaded with gang, narrow pedestrian alleys that never appear on a car map. A gated perumahan is easy to drive into, but it has one or two entrances and dead ends everywhere. Measured by car alone, both come out wrong, in opposite directions.
I built Bentuk Kota for urbanists, planners, OpenStreetMap mappers and curious readers. It measures the same disc of the same neighbourhood twice, once as a driving network and once as a walking network, and puts them side by side. The gap between the two is the finding.
Kampung versus perumahan carries class overtones in Indonesia. So I set one rule for the whole product: it describes and never scores. There is no ranking, walkability grade or liveability index anywhere.
What I built
I wrote the street maths myself
The measurements are the project, so I didn't import a graph, chart or map library. Building the networks, reading street bearings, and computing entropy, detour ratios and junction counts are all my own code. I wrote it from the paper, with its sections cited. The measuring code takes numbers in and gives numbers out, so the same code runs in the data pipeline and in the tests.
Tests use streets with known answers
Nobody knows the true entropy of Bendungan Hilir, so the tests don't pretend to. They build networks whose answers are known in advance: a perfect grid, the same grid rotated 29°, a random network and a pure tree. Then they check that the measured numbers come back right. Every chart, synthetic or real, must also pass basic maths checks, such as looking the same when turned 180°.
The biggest assumption is a switch readers can flip
Which OpenStreetMap tags count as drivable or walkable changes every number on the site. I put that choice in one documented place and exposed it on an assumptions page, as a switch between three mappings. Readers can see how much each conclusion depends on it.
I picked sites by measuring map coverage
My first plan was a hand-picked list. Two rounds of guessing produced 10 usable sites from 49 guesses, and the misses proved nothing. So I wrote a sweep across 56 cities that ranks candidate areas by how many footpaths are actually mapped. It found 14 of the final 33 sites, including the first ones in Papua, Lombok and Kalimantan.
I wrote rules for where coverage misleads
Mapped footpaths can't tell an alley from a garden path. Bogor's best-covered candidate, at 40.9%, was a botanical garden. Jakarta's best was 78% inside one park. I turned those lessons into rules. I also added a check that the survey must predict the pipeline's result within one percentage point, which caught two sites entered with stale coordinates.
Result
Bentuk Kota is live and public, in Indonesian and English. It covers 33 sites, each measured as two networks under three tag mappings, chosen from 126 candidate centres surveyed across 56 cities; 22 of those candidates were rejected and published anyway, with their reason stated rather than deleted. Every site shows how well its alleys are mapped, and 9 of the 33 are flagged for thin coverage so missing map data can't fake the finding.
That limit reaches the headline comparison. Every gated-housing candidate came back thin, at 0.9–4.4% coverage, so the kampung versus perumahan comparison rests on two readable gated sites out of three. The page states that number. The site makes no network calls once it loads. I built the first version in eight days in August 2026, then kept working on it into late September.
Under the hoodTechnical detail for engineers
- Method: Boeing (2019) orientation entropy on 36-bin bearing histograms ("roses"), the φ orientation-order indicator, sampled circuity, node-degree statistics and footway coverage. The dual drive/walk application is original.
- The measurement core in
lib/morphology/(1,029 lines) is pure TypeScript: no DOM, no React, no clock, no network. - Synthetic tests: perfect grid, grid rotated 29°, random graph, pure tree. Invariants asserted on every histogram: 180° symmetry, circuity ≥ 1, degree proportions summing to 1.
- The OSM tag mapping lives in one module with 3 switchable mappings; sensitivity is reported on
/asumsi. Method, limits and ODbL attribution are on/metode. - Coverage rules: a park share above 25% is not urban fabric;
landuse=grassis fetched and ignored because mappers use it for roadside verges. - Data comes from targeted Overpass queries at build time, cached and rate-spaced. The PRD specified the ~1 GB Geofabrik Indonesia PBF; 33 one-kilometre discs are a few megabytes, and reading PBF would have meant a protobuf dependency for data used once.
- Next.js 14 static export on GitHub Pages. Four runtime dependencies (Next.js, React, React DOM, Zod). Cards, sorting, density modes and cross-card highlights are server-rendered HTML and CSS with state in the URL; the only client component is a small nav-state helper.
- The plate exports at ~4.7 MB of HTML, ~59% of it the App Router flight payload. Ruler ticks were trimmed from 165 to 66 bytes each; the rest is the collapsed data tables kept for screen readers.
- Contrast ratios and the type scale are recomputed by unit tests from the hex values (
palette.test.ts,type-scale.test.ts). A dedicated test stops the distribution ruler from reading as a ranking. - 585 tests in 16 files. Synthetic, invariant and data-validation suites plus an accessibility audit gate every deploy.
- Bilingual (id/en), installable as a PWA, all milestones M0–M6 shipped.
- About 13,840 lines of TypeScript, 101 commits between 20 August and 27 September 2026.
Screenshots
Hiring, or building something similar?
I'm open to full-time roles and select freelance work.