Gentrification & Housing — Data Project

When a
Neighborhood
Changes,
Who Pays?

This project investigates the demographic and income patterns driving urban displacement across a study population of 9.75 million — exposing the fault lines between who benefits from neighborhood change and who cannot afford to stay.

"Gentrification is not random. It follows the predictable geography of concentrated wealth, moving into neighborhoods where longtime residents cannot compete on income alone."

— Urban Change Lab · 2024
9.75M
Total Population
Study area
Districts
8 CCDs
LA County
Highest Rent
2024 median
Income Gap
Highest vs lowest district
Photo — Neighborhood Street Scene

↑ Add photo: A street-level view of a neighborhood in transition — older homes alongside new construction.

Hispanic residents in South Gate–East LA
The highest Hispanic share of any district — over 95% of its total population, making it the most concentrated working-class Latino community in the dataset.
Rent gap: Santa Monica vs South Gate
The dollar difference in median monthly rent between the most and least expensive districts in 2024 — two communities, one housing market.
Rent growth 2016→2024, highest district
Even the lowest-income districts saw rent rise faster than incomes over this period — the core displacement mechanism.
About This Project

Understanding Urban Displacement Through Data

Gentrification reshapes cities neighborhood by neighborhood — changing who can afford to live where, and who gets left behind. This project draws on census data to quantify the demographic and income patterns that drive urban displacement across LA County's 8 Community College Districts.

By examining racial composition alongside income and rent data, we can see how structural economic inequality maps directly onto housing pressure — and who, ultimately, pays the price of change.

Use the navigation above to explore the raw data, read the planned interactive narrative, and view our summary visualizations.

Photo — Historic Housing Stock
Before: Original neighborhood housing — add photo here
Photo — New Development
After: New development — add photo here
"The data doesn't lie: neighborhoods with the highest concentration of working-class renters and income inequality are the most vulnerable to rapid, irreversible change." — Urban Change Lab · Research Summary · 2024
Annual income gap between highest and lowest district median, 2024
Live Data — 3 Tables
Core Data Sets

All figures pulled live from the ddelgatt_LA_neighborhoods MySQL database across three tables: demographic, income, and medianRent. Every number here is directly from the source.

Table 1 · demographic

Race & Ethnicity by District, 2024

Population composition across all 8 LA County CCDs. Data shows 2024 estimates for Hispanic/Latino population and non-Hispanic race groups from the demographic table.

Loading from database
Table 2 · income

Household Income by District, 2024

Median and mean household income per district from the income table, sorted highest to lowest. Shows share of households at the top and bottom income brackets.

Loading from database
Table 3 · medianRent

Median Rent by District — Selected Years

Median gross rent from the medianRent table for 2016, 2019, 2022, and 2024. Click any column header to sort. Los Angeles district row is highlighted.

Loading from database
M3 Storyboards — Narrative Sequence
The Cost Is Not
Evenly Distributed

A scripted, interactive narrative sequence guiding users through LA County's displacement crisis — from demographic portrait to income stratification to scenario modeling. Seven frames, approximately 4 minutes of guided experience.

Sequence Arc
Hook → Context → Data → Reveal → Interact → Model → CTA
7
Sequence frames
~4 min
Total runtime
3
Interactive moments
Scroll
Primary trigger

The interactive narrative is designed as a scroll-triggered sequence: the user moves through scenes one at a time, with charts building, numbers animating, and annotations appearing as they scroll. Every moment is intentional.

The goal is that by the end, the reader understands not just what the numbers say — but what they mean for the people who live inside them.

Planned Feature  Full interactive build coming in the final milestone.

Sequence Timeline
01
Opening Hook
0:00 – 0:30
02
Who Lives Here
0:30 – 1:00
03
Age → Risk
1:00 – 1:30
04
Income Split
1:30 – 2:15
05
The $66K Gap
2:15 – 2:45
06
Scenario Model
2:45 – 3:30
07
Call to Action
3:30 – 4:00
Sequence 01 Opening Hook — "Someone Gets Pushed Out" 0:00 – 0:30 · Auto-play on load
LA County · 9.76M People
Every year,
someone gets
pushed out.
Frame 1A · 0:00
Cold open — paper screen, text fades in
Duration: 3s · Trigger: page load
Page loads to full paper background. Eyebrow text fades in first (0.2s delay), then headline appears in Playfair Display (0.5s delay). No other UI visible yet.
CSS fade-instaggered
Animation: opacity 0→1, translateY 6px→0, easing: easeOutQuart
Who is most at risk?
25–34
Peak renters
35–44
Family renters
Age cohorts by population share
Frame 1B · 0:08
Stat cards slide in, key cohorts highlighted
Duration: 5s · Trigger: 3s after load
Two stat cards slide up from below (staggered 150ms apart). Age bar chart draws in from left. The 25–34 and 35–44 bars render red; others at low opacity.
slide-upbar draw
Bars animate width 0%→actual over 800ms. Peak bars red, others at 30% rule-color opacity.
Scroll to begin
This is a story about who pays the price for a city's growth.
Frame 1C · 0:18
Scroll prompt appears — user invited in
Duration: holds until scroll · Trigger: 8s after load
Subheadline and scroll indicator fade in. Pulsing red line indicator shows scroll direction. Nav bar appears sliding down from above.
Scroll → Seq 02
User must scroll to advance. Creates intentional pause before data begins.
Sequence 02 Who Lives Here — Population Pyramid 0:30 – 1:00 · Scroll-triggered
Age & Sex · LA County
Frame 2A · 0:30
Pyramid builds from center out
Duration: 1.5s · Trigger: scroll into view
Population pyramid animates — bars grow from 0 width to full, radiating from middle cohorts. Peak bars (25–34) render red; adjacent cohorts in dimmed red; all others rule-color.
width 0→full800ms stagger
Each row delays 60ms. 25–34 rows render red at full; others render muted.
25–34 yrs → highest risk
Cohort spotlight
1,500,708
residents aged 25–34 · 15.4% of county
Frame 2B · 0:42
Annotation callout appears on peak cohort
Duration: 2s · Trigger: 1.5s after 2A
A red annotation box appears on the 25–34 bar. A "cohort spotlight" card slides up with raw population count in Playfair Display. All other bars dim further.
slide-up carddim others
Annotation fades in with slight bounce (scale 0.9→1.0). Card slides from y+20px.
Key insight
2.9 million people in peak renter age brackets
These are the residents most exposed to rental market pressure — they need stable housing the most, and are the first to be displaced when rents rise.
↓ Scroll to see income data
Frame 2C · 0:52
Insight card + scroll prompt to Seq 03
Duration: holds until scroll · Trigger: 2s after 2B
Text panel fades in with key insight in Playfair Display. Scroll prompt pulses softly at bottom. Deliberate pause — user reads before proceeding.
Scroll → Seq 03
No skip button — linear narrative required.
Sequence 03 Age → Risk Link — Connecting Demographics to Vulnerability 1:00 – 1:30 · Scroll-triggered
Age cohort size
95
Risk score · 25–34
Cohort size × income pressure = displacement score
Frame 3A · 1:00
Risk formula revealed — two panels side by side
Duration: 2s · Trigger: scroll into view
Two panels slide in from opposite sides. Left shows age cohort size bars (red peak); right shows the risk score in large Playfair numerals. Formula caption fades in below.
side-by-side reveal
Left slides from x-40, right from x+40. 600ms, easeOutQuart.
Click a cohort to explore
25–34
15.4%
35–44
14.4%
20–24
6.4%
45–54
12.8%
Selected: 25–34
Risk: 95/100
Frame 3B · 1:10
First interactive moment — user clicks cohorts
Duration: user-controlled · Trigger: 2s after 3A
Cohort cards become clickable. 25–34 pre-selected in red. User taps any cohort to update the risk score and context panel. First deliberate handoff of control.
Interactive: click any card → risk score count-up animation.
score count-upuser click
Nearly 1 in 3
residents is a
potential
DISPLACEMENT RISK
But the risk is not equal.
It depends on your income.
Frame 3C · 1:22
Bridge statement — pivot to income data
Duration: 3s · Trigger: 8s after 3B (or scroll)
Bold serif bridge statement appears. "Potential" is in rule color — deliberately ambiguous. Red bordered label. Final line pivots to income as the determining variable.
Scroll → Seq 04
Transition: page cross-fades. 400ms ease.
Sequence 04 Income Split — Four Household Types, Four Realities 1:30 – 2:15 · Scroll-triggered with interactive filter
Rows: All HH · Nonfamily · Married-Couple | Cols: Low→High income
Frame 4A · 1:30
Heatmap appears — red-intensity encoding
Duration: 1.2s · Trigger: scroll into view
Income heatmap reveals using red intensity — high concentration = deep red, low = warm sand. Cells draw left-to-right with 80ms stagger. The contrast between household types is immediate.
cell-by-cell reveal
Cells fade in opacity 0→target, staggered left-to-right.
Isolate household type
Nonfamily
Married
All
<$25k
25.6%
$25–50k
17.0%
>$100k
21.6%
Frame 4B · 1:42
Second interactive moment — isolate household type
Duration: user-controlled · Trigger: 1.2s after 4A
Button group appears. Default: Nonfamily. User switches to Married-Couple to see the inverted distribution. Chart re-renders animated on each switch.
Interactive: button click → heatmap row highlights, bar chart animates.
user clickchart re-render
The contrast
Nonfamily HH
25.6%
earn under $25k
Married-Couple
28.3%
earn over $200k
These two groups share the same county.
They do not share the same risk.
Frame 4C · 2:05
Contrast callout — two groups, same city
Duration: 3s · Trigger: 8s after 4B (or scroll)
Two stat panels animate in — nonfamily in red-bg, married-couple in white. The closing line uses italic. A moment of editorial restraint before the climactic gap reveal.
simultaneous fade-inScroll → Seq 05
Sequence 05 The $66K Gap — Core Finding, Full Focus 2:15 – 2:45 · Auto-play, climactic moment
$66K
Median income gap
$66,184
Between married-couple families
and nonfamily households
Frame 5A · 2:15
Big number — count-up on dark background
Duration: 2s · Trigger: scroll into view
The one fully dark section — the ink background makes the number hit harder after the warm paper aesthetic. $66,184 counts up from $0 over 1.8s. Watermark ghost of $66K behind it.
count-up JS
Counter: 0→66184 over 1800ms, easeOutExpo. Font scales 32px→36px during count.
Median income
Married-Couple
$127,806
Nonfamily
$61,622
← Gap: $66,184 →
Frame 5B · 2:25
Gap bars — difference made spatial
Duration: 1.5s · Trigger: 2s after 5A
On the same dark background, two horizontal bars reveal the gap. Married-couple bar draws in paper color first; nonfamily bar draws to 48% width. Gap bracket animates in last.
sequential bar draw
Married: 0→100% in 600ms; nonfamily: 0→48% in 400ms; then bracket fades.
In a county where median rent for a 1-bedroom is approximately $2,200/month, a household earning $61,622/year spends 43% of income on rent.
Rent burden
43%
Nonfamily median · HUD threshold: 30%
Frame 5C · 2:38
Contextual anchor — rent burden stat
Duration: holds until scroll · Trigger: 1.5s after 5B
Back to paper background — relief after the dark moment. 43% figure in red Playfair appears with count-up. Red-bg stat card lands the human cost before the scenario modeler.
count-up 43%Scroll → Seq 06
Sequence 06 Scenario Modeler — "What Would Change?" 2:45 – 3:30 · Fully interactive
Adjust the scenario
Married-Couple
$127k
Nonfamily
$61k
Current gap
$66,184
Frame 6A · 2:45
Scenario sliders introduced
Duration: user-controlled · Trigger: scroll into view
Sliders with red thumb handles slide in. Real data pre-loaded. "What would it take to close the gap?" Gap figure in Playfair Display updates live below sliders.
slide-in Interactive: drag sliders → gap updates live.
Current
Gap: $66,184
Your Scenario
Gap: $44,000
↑ Gap narrowed by $22k in this scenario
Frame 6B · 3:00
Before/After view — impact of scenario
Duration: live · Trigger: user slider interaction
Side-by-side appears once user moves a slider. Left = current (locked), right = scenario in red-bg with live-updating values. Gap delta shown below.
Interactive: bars animate on each slider change. Gap delta with +/- prefix.
Preset scenarios
Minimum wage increase to $25/hr
Nonfamily median → ~$78k · Gap → $49k
Rent control cap at 5% annual increase
Effective nonfamily burden relief · Gap → $61k
Income stagnation — no change
Gap holds at $66k → displacement accelerates
Frame 6C · 3:12
Preset policy scenarios — scripted data changes
Duration: user-controlled · Trigger: 8s after 6A
Three preset cards — active one highlighted in red-bg. Click snaps sliders to scenario data and animates charts. Fulfills M3/M4 "scripted visualization changes" requirement.
Interactive: click card → sliders animate → charts re-render → gap updates.
slider animatechart re-renderuser click
Sequence 07 Call to Action — "Data Doesn't Displace People. Policy Does." 3:30 – 4:00 · Auto-play closing
Conclusion
Data doesn't
displace people.
Policy does.
Frame 7A · 3:30
Closing statement — staggered Playfair reveal
Duration: 3s · Trigger: scroll into view
"Data doesn't displace people" appears bold first. Then "Policy does." fades in as italic Playfair in rule color — implying complicity through the dimming.
CSS stagger
Line 1: 0.6s delay, bold. Line 2: 1.2s delay, italic, rule color.
What we found
29.8% of residents are in peak renter age brackets (25–44)
$66,184 median income gap between married-couple and nonfamily households
Nonfamily households spend 43% of income on rent — 13 points above HUD's burden threshold
Frame 7B · 3:42
Three findings bullet in sequentially
Duration: 2.5s · Trigger: 3s after 7A
Three findings appear one by one with 600ms stagger. Dot colors escalate: rule → muted → red. Third finding is the most damning and in ink color.
staggered slide-in
Dot scales in (200ms), then text slides from x-12px (300ms). 600ms between each.
Explore the full data
→ View full data tables
→ Explore interactive visualizations
Data: U.S. Census Bureau ACS · ACAD 274
Frame 7C · 3:52
Navigation links — send user to full site
Duration: end state · Trigger: 2.5s after 7B
Two navigation CTAs. First link has ink border (primary). Second dimmed in rule color. End of narrative — user can explore freely.
links to site pages
Primary CTA: ink border, full color. Secondary: rule border, muted.
Implementation Notes

Technical requirements & approach for Final Build

Notes for M4 implementation.

Scroll Trigger
Intersection Observer API
Use the native IntersectionObserver to detect when each sequence section enters the viewport. Add a CSS class (e.g. .in-view) which triggers animation CSS. No library needed — pure vanilla JS.
Chart Animations
Chart.js animation config
Chart.js has built-in animation on first render. For interactive re-renders (sliders, button toggles), use chart.update('active') with animation duration 300–500ms. Easing: easeInOutQuart.
Count-up Animation
JS counter + easing
Simple requestAnimationFrame loop: start=0, end=target, duration=1800ms. Apply easeOutExpo to the interpolation. Trigger on IntersectionObserver. Used for $66,184 (Seq 05) and 43% rent burden.
Live Data
Fetch from DB on load
Narrative data (median incomes, population figures) should be fetched from the live database via a PHP endpoint on page load. Scenario modeler seeds from live data so the narrative updates if the DB updates.
Preset Scenarios
Hard-coded policy data
The three policy scenarios (Seq 06C) are hard-coded JSON objects. They do not come from the DB — they are research-backed hypotheticals. Label each one clearly as modeled scenario to distinguish from real data.
Accessibility
Respect prefers-reduced-motion
Wrap all animation-triggering code in a check: matchMedia('(prefers-reduced-motion: reduce)'). If true, skip animations and show content immediately. All charts must be accessible without animation.
Visual Analysis — Live Charts
Summary Findings

Six charts built from live database data — demographics, income, and rent across all 8 LA County CCDs. All data from ddelgatt_LA_neighborhoods.

Visualization 1 · demographic table

Race & Ethnicity by District

Each bar is one district. Segments show Hispanic/Latino, NH White, NH Black, NH Asian, and other populations as a share of total. Hover for counts.

Visualization 2 · demographic table

District Composition — Select a District

Pick a district to see its racial breakdown as a donut.

Visualization 3 · demographic table

Hispanic Population by District

Total Hispanic/Latino residents per district. Hover for exact count and % of total.

Visualization 4 · income table

Median Income by District, 2015–2024

Click district buttons to show/hide lines. Hover for exact median income per year.

Visualization 5 · medianRent table

Median Rent Over Time, All Districts

Click district buttons to toggle lines. Hover for exact rent values.

Visualization 6 · medianRent table

% Rent Increase 2016→2024

Which districts saw the biggest rent growth? Hover bars for exact % change.

Visualization 7 · medianRent table

Rent by Bedroom Size — Pick a District & Year

How much does bedroom count cost you? Pick any district and year to see how rent scales from studio to 5+ bedrooms. Useful for understanding whether families — not just single renters — can afford to stay.

District
Year
Select a district above.