Interactive 3D globe for exploring World Bank development indicators, CO₂ emissions, and bilateral migration corridors.
Built with React Three Fiber, FastAPI, and MongoDB. Designed as a senior-portfolio-grade demonstration of real-time geospatial rendering, async data pipelines, and modular TypeScript architecture.
GlobalTrace/
├── api/ # FastAPI backend
│ ├── app.py # Application factory, CORS, lifecycle
│ ├── models.py # Pydantic v2 data contracts
│ ├── requirements.txt # 8 production dependencies
│ └── routes/
│ ├── indicators.py # World Bank proxy + 24-hr MongoDB cache
│ ├── countries.py # REST Countries proxy + 7-day cache
│ └── migration.py # Static UN DESA 2020 corridor data
│
└── web/ # Vite + React 18 frontend
└── src/
├── core/ # Pure logic — no React, fully testable
│ ├── store.ts # Zustand app state
│ ├── data.ts # API client + O(1) index builder
│ ├── hooks.ts # Data-fetching React hooks
│ ├── palette.ts # D3 colour scale builder (log + linear)
│ ├── geo.ts # Spherical math + cubic Bézier arcs
│ ├── format.ts # Number/currency formatters
│ ├── layers.ts # Indicator registry
│ └── isoLookup.ts # ISO 3166 numeric→alpha-3 map
├── globe/ # Three.js / R3F scene
│ ├── GlobeCanvas.tsx # Canvas root + EffectComposer
│ ├── CountryLayer.tsx # Canvas-texture country coloring
│ ├── HitSurface.tsx # Pointer-event hit detection
│ ├── FlowArcs.tsx # Animated migration arc lines
│ ├── Halo.tsx # Fresnel atmosphere shader
│ ├── Starfield.tsx # Point-cloud star background
│ ├── OceanSphere.tsx # Base ocean mesh
│ ├── Orbit.tsx # OrbitControls wrapper
│ ├── mesh.ts # BufferGeometry builders
│ └── glsl.ts # Inline GLSL shaders (atmosphere + arc)
├── panels/ # HUD components
│ ├── Header.tsx # Layer switcher, arc toggle, theme flip
│ ├── Sidebar.tsx # Search + region filter
│ ├── DetailDrawer.tsx # Country detail + sparkline
│ ├── MiniChart.tsx # Pure-SVG sparkline renderer
│ ├── Timeline.tsx # Year scrubber + playback
│ ├── ColorKey.tsx # Percentile-based legend
│ ├── CursorInfo.tsx # Hover tooltip
│ └── Splash.tsx # Loading screen
├── shell/
│ └── Shell.tsx # Top-level layout orchestrator
└── theme/
├── variables.css # Design tokens (dark + light)
└── base.css # Reset, typography, layout primitives
Country boundaries are drawn onto an OffscreenCanvas and uploaded as a CanvasTexture per frame rather than rendering hundreds of individual <mesh> elements. This keeps the GPU draw-call count at 1 regardless of the number of countries, making the globe scroll at 60 fps on integrated graphics.
The raw World Bank API response (~20,000 data points) is indexed on receipt into a nested Map<iso3, Map<year, value>> via core/data.ts:indexResponse(). During the render loop, per-country values for any year are retrieved in O(1) with zero array scanning.
The atmospheric glow uses a custom GLSL Fresnel term rather than a pre-baked texture. The rim intensity follows pow(1 - dot(normal, eye), falloff), making the halo scale correctly at any zoom level.
Migration corridor arcs are computed in core/geo.ts:traceArc() as cubic Bézier curves. Control points are lifted proportionally to the great-circle arc angle, so short hops stay low while long corridors (e.g. Mexico → USA) get a dramatic atmospheric bulge. A custom GLSL shader animates a pulse along each arc using a uTime uniform.
| Route | Source | Cache TTL |
|---|---|---|
/api/worldbank/indicator/:code |
World Bank API | 24 hours |
/api/countries |
REST Countries API | 7 days |
/api/migration |
Static (UN DESA 2020) | — |
All cache entries use MongoDB replace_one(upsert=True). On upstream failure the API serves the stale cache rather than returning a 502.
All interaction state (selected country, year, layer, camera target, theme) lives in a single flat Zustand store (core/store.ts). Components subscribe to individual slices, avoiding unnecessary re-renders.
| Layer | Technology |
|---|---|
| 3D rendering | Three.js + React Three Fiber |
| Post-processing | @react-three/postprocessing (Bloom) |
| State | Zustand |
| Data fetching | Axios |
| Colour scales | D3-scale + D3-interpolate |
| Geo math | d3-geo, topojson-client, world-atlas |
| Backend | FastAPI + Motor (async MongoDB) |
| Database | MongoDB (local or Atlas) |
| Build | Vite 6, TypeScript 5 |
cd api
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
# create api/.env
echo "MONGO_URL=mongodb://localhost:27017" >> .env
echo "DB_NAME=globaltrace" >> .env
echo "CORS_ORIGINS=http://localhost:3000" >> .env
uvicorn api.app:app --reload --port 8000cd web
npm install
# create web/.env
echo "VITE_API_URL=http://localhost:8000" >> .env
npm run dev # → http://localhost:3000cd web
npm test| ID | World Bank Code | Scale |
|---|---|---|
| GDP (current US$) | NY.GDP.MKTP.CD |
logarithmic |
| CO₂ emissions | EN.GHG.CO2.MT.CE.AR5 |
logarithmic |
| Population density | EN.POP.DNST |
logarithmic |
| Life expectancy | SP.DYN.LE00.IN |
linear |
All indicators cover 2000–2024 and are fetched lazily on first layer selection.
| Service | Platform | Notes |
|---|---|---|
| Frontend | Vercel | Set VITE_API_URL in project env vars |
| Backend | Render | Set MONGO_URL, DB_NAME, CORS_ORIGINS |
| Database | MongoDB Atlas | Free M0 tier sufficient for caching workload |
MIT
