LA's 146-Year Building Boom Mapped Permit by Permit

Los Angeles Airways Sikorsky S 61L N303Y screenshot (4)

I've been staring at this visualization of Los Angeles's building stock for longer than I care to admit, and it's doing something unexpected to my brain. Give each building its birth year, hit play, and suddenly 146 years of building permits stop reading like a single path to sprawl and start looking like a ledger of booms, slumps, and streetcar geometry, all visible in one frame. The tallest structures drag to pan across the cityscape, revealing how height restrictions and market crashes leave their mark in very literal ways.

What's missing from this view is just as telling as what's there. Those torn-down buildings—the original downtown hotels, the bungalow courts razed for parking lots—aren't included. So what emerges isn't Los Angeles as it was, but Los Angeles as it survived. The visualization becomes a record of persistence rather than ambition.

The interface itself feels deliberately unflashy: one box per building, appearing in the year it was constructed, with the median story height hovering around sixteen feet. You can tilt the view with a control-drag, pan with a right-drag, and slowly piece together how policy decisions from 1920s zoning ordinances still shape what you're looking at today. It's the kind of tool that makes you reconsider what urban history looks like when you strip away the narrative we've been told about Los Angeles sprawl.

The data: giving every building a birth year

Construction year data doesn't live in one clean database. It's scattered across assessor parcel records, digitized building permit cards, and historic map overlays that predate digital record-keeping. When you pull it all together, you're making a best-effort timeline, not a definitive one.

The gaps aren't random. Permit records from the 1970s often survived because they were already on microfiche. Records from the 1950s? Those were on paper cards that got lost when the city moved offices twice in six years. Historic maps help fill in the blanks, but they show footprints, not construction dates. A building drawn on a 1920 map might have been built in 1918 or 1922. The data can tell you the year within a window, but not the exact month.

This matters for the map. Right now, the visualization treats the data as precise. A building tagged 1925 appears in a 1925 bucket, even if the actual construction could have happened anywhere from 1922 to 1928. Users notice. One tester pointed out that the ocean shows up as empty space while land has buildings, which makes the age distribution look skewed. Another noted that the map only shows buildings still standing, making older periods look artificially empty.

Here's how the data pipeline works:

def merge_building_years(parcel_data, permit_data, map_data):
    merged = {}
    
    for bbl in set(parcel_data) | set(permit_data) | set(map_data):
        sources = []
        if bbl in parcel_data:
            sources.append(('parcel', parcel_data[bbl]))
        if bbl in permit_data:
            sources.append(('permit', permit_data[bbl]))
        if bbl in map_data:
            sources.append(('map', map_data[bbl]))
        
        # Take earliest year as "first known appearance"
        merged[bbl] = {
            'year': min(s[1] for s in sources),
            'confidence': 'high' if len(sources) > 1 else 'low',
            'sources': [s[0] for s in sources]
        }
    
    return merged

The result is a dataset that's useful but honest about its limitations. Buildings from the 1980s onward have paper trails you can follow. Anything older is a patchwork of clues, and the map should reflect that uncertainty rather than hiding it.

The crisp signals of 146 years of growth

The 146-year growth pattern of Los Angeles reads like a geological record, each era leaving a distinct sedimentary layer in the urban fabric. If you overlay property ages on a map, the story becomes visible at a glance.

Pre-1900 construction hugs the original pueblo site and the early rail corridors like veins. You'll find clusters of Victorian-era houses in areas that later became major thoroughfares, but they're sparse — the city hadn't spilled far beyond its founding footprint. The 1920s boom looks different: instead of following rail lines, developers pushed the grid outward through small-lot subdivisions. These aren't the wide boulevards of later planning — they're narrow strips, often just 25 feet wide, sold as "investment opportunities" to buyers who'd build a house and rent out the backyard unit.

Then there's the post-WWII jump. Where earlier growth crept incrementally, this period hits like a switch. The San Gabriel Valley, the San Fernando Valley, parts of the South Bay — they go from agricultural land to sudden grids of identical lots almost overnight. On a density map, these areas appear as sharp vertical walls of development, not gradual gradients. It's the visual signature of mass production housing on previously undeveloped land.

The final decades tell a story of diminishing returns. Infill development fills in the gaps, but the scale is smaller. New clusters rise around transit stations — Metro Gold Line in Pasadena, the Expo Line in Santa Monica — but they're measured in single blocks, not whole valleys. The growth rate flattens, and the map reflects that: denser patches amid older fabric, but nothing that reshapes the overall silhouette.

One viewer noted the ocean was missing from the analysis, which is fair. Another pointed out that demolished buildings disappear from the record entirely, making older periods look artificially empty. Those criticisms are valid, but they don't change the core insight: Los Angeles didn't grow uniformly. Each era built differently, and those differences are still readable in the city's bones.

import geopandas as gpd
import matplotlib.pyplot as plt

parcels = gpd.read_file("la_parcels.geojson")

parcels['decade'] = (parcels['year_built'] // 10) * 10
parcels = parcels[parcels['decade'] < 2020]  # Exclude recent incomplete data

fig, ax = plt.subplots(figsize=(12, 10))
parcels.plot(column='decade', ax=ax, legend=True, cmap='viridis', 
             legend_kwds={'label': "Construction Decade"})
ax.set_title("Los Angeles Property Ages, 1870–2010")
plt.savefig("la_growth_map.png", dpi=150)

Turning millions of footprints into smooth playback

What strikes me about this work isn't just the technical execution—it's what happens when you treat static building footprints as a time machine. The team stitched together 1.1 million 3D structures with assessor records, and suddenly you can watch the San Fernando Valley fill in after the war, block by block, lot by lot. That's the kind of thing that gets buried in spreadsheets until someone builds the right interface around it.

The data engineering here is unflashy but solid: spatial joins to match buildings with parcels, vector tiling to keep the browser from choking, client-side rendering that actually responds to zoom without loading screens. I've seen projects like this collapse under their own geometry, so credit where it's due. But here's what I keep thinking about—the analytical payoff. You can see the 1960s building boom radiate outward from downtown, and the height restrictions that kept the city low-rise until the 1980s hit different when you can pan around and watch the skyline change.

Still, this also exposes a hard ceiling. Static footprints tell you where buildings are, not when they went up unless you cross-reference assessor data. And they tell you nothing about the people who lived there, the rents they paid, or the blocks that got bulldozed for urban renewal. The visualization is sharp, but it's still looking at the city through a keyhole. I'd love to see what happens when you layer in permit data, demographic shifts, or transit infrastructure—this feels like the foundation for something bigger, not the finished product.

The million-frame trick and its mirror

What strikes me about this work isn't the scale — though 1.1 million footprints is genuinely impressive — but the way it makes you confront what static building data actually captures versus what it misses. The visualizations do real analytical work here. You can see the post-war sprawl of the San Fernando Valley, the gradual filling in of downtown's height restrictions, the slow creep of denser development along transit corridors. These aren't just pretty maps; they're arguments about how Los Angeles grew, told through geometry and timing.

But the project's honesty about its own limitations is what gives it weight. Building footprints are a point-in-time snapshot, frozen at whatever moment the assessor last updated them. A 1950s ranch house and a 2020 teardown that now holds a three-story apartment both register as static shapes. The temporal story this tells is real, but it's also incomplete — filtered through the gaps in municipal data collection, the lag between construction and assessment, the buildings that existed but never got formally recorded. I think this underestimates the friction of working with public sector data, where completeness is always aspirational.

The technical execution holds up under scrutiny. Spatial joins across that volume of data, vector tiling for client-side performance, the rendering choices that keep it interactive — these aren't gimmicks. They're the kind of infrastructure work that usually stays invisible, but here it's the thing that makes the analytical claims legible. You can't see development patterns without first solving the problem of making a million polygons behave in a browser.

What I'm left wondering is how transferable this is. The same pipeline applied to a city with less systematic assessment data, or one where informal construction dominates, would tell a very different story — or no story at all. The insights feel specific to Los Angeles's particular bureaucratic rhythms, which makes me skeptical of treating this as a universal model for urban analysis.

Conclusion

The visualization strips away what was demolished, leaving only what survived. That itself is a kind of bias — the city as it stood in 2020, not as it existed in 1880 or 1950 or 1980. Every dot on this map earned its place by outlasting something else.

What's striking isn't how much grew, but how unevenly. Some blocks absorbed dozens of teardowns and rebuilds; others held steady for a century and a half. The data doesn't explain why — zoning, economics, and plain luck all leave their mark, but this map just shows the scars.

I'm still not sure what to make of a city that can be reduced to a single height field and still feel alive. Maybe that's the point.