Back to projects
2026Live

Atlas

A 3D globe that rotates to your city and tells you the weather.

ReactThree.jsReact Three FiberTanStack StartTanStack QueryFramer MotionGSAPTypeScriptTailwind CSSOpen-Meteo
Atlas screenshot

Weather Data Is Boring. A Spinning Globe Isn't.

Every weather app shows you a card with a sun icon and a number. They all look the same because the data is the same — the differentiation has to come from how you present it. The idea here was to make the location feel physical: when you search a city, the Earth actually rotates to put it in frame. The weather is almost secondary to that moment.

The API choice was a constraint worth celebrating. Open-Meteo is free, keyless, and covers current conditions, hourly and 7-day forecasts, marine wave data, and AQI — everything in one place without requiring an account or shipping secrets to the client. That made the app genuinely deployable for anyone.

Globe First, Data Second

The core interaction is a search that drives two things simultaneously: the globe animates to the searched coordinates via a smooth camera arc, and a TanStack Query request fires against Open-Meteo's geocoding and weather endpoints. The weather panel slides in once the globe settles. It's a single user action with two parallel effects.

The data layer covers more ground than a typical weather widget: current conditions with feels-like and humidity, a 24-hour chart built in Recharts, a 7-day forecast strip, marine conditions for coastal queries (wave height, period, swell direction), and an AQI breakdown with individual pollutant values. Points of interest from OpenStreetMap fill out the 'nearby' tab. None of it needs a backend — TanStack Start's server functions handle the API proxying, but there are no databases and no keys to manage.

Search → Globe Rotates → Panel Opens

A three-act UI: the idle globe, the search-and-navigate transition, and the data panel. Each act has its own animation budget. The globe is always the hero; the data panel never obscures it entirely.

01
3D Globe

Three.js Earth with day/night texture cycle, cloud layer, and atmospheric glow. Idle rotation slows when a location is focused. React Three Fiber manages the scene tree; Drei handles the environment.

02
Global Search

⌘K / Ctrl+K command palette pattern. Open-Meteo geocoding returns candidates; selecting one triggers the globe rotation arc and fires the weather request in parallel.

03
Weather Panel

Slides in from the right after the globe settles. Tabs: Current, Hourly (Recharts line chart), 7-Day, Marine, AQI, and Nearby. Framer Motion handles the panel mount/unmount.

04
Geolocation

One-click button calls the browser Geolocation API and jumps the globe to the user's coordinates without a search.

05
Marine + AQI

Coastal queries surface wave height, period, and swell direction. AQI tab shows the composite index and a breakdown of PM2.5, PM10, NO₂, ozone. Both gracefully absent for landlocked or low-pollution locations.

Under the Hood

TanStack Start + React Query

TanStack Start provides file-based routing with server functions for API proxying. React Query manages all weather fetches — stale-while-revalidate means cached data appears instantly while fresh data loads in the background. No useEffect data fetching anywhere.

Three.js + React Three Fiber

Earth is a sphere with a day/night texture blend driven by the sun position vector, a cloud layer mesh slightly offset from the surface, and a custom atmosphere shader. Globe rotation to a target lat/lng is a GSAP-driven camera arc with easing.

GSAP + Framer Motion

GSAP owns the globe camera arc and any timeline-sequenced transitions. Framer Motion handles React tree animations — panel mounts, tab transitions, list staggering. Kept them in separate lanes to avoid conflicts.

Open-Meteo APIs

Single API suite covering weather, marine, air quality, and geocoding. All requests are keyless and CORS-friendly. TanStack Query's query keys encode the coordinates and metric type, so switching cities never serves stale data from a previous location.

What Made It Hard

  • Rotating the globe to precise coordinates required converting lat/lng to a 3D spherical position, computing a camera arc that stays outside the globe surface throughout the animation, and landing with the target city centered in the viewport — not just facing the camera. Getting the arc to feel cinematic rather than mechanical meant adding a slight altitude overshoot at the midpoint via a custom GSAP ease.
  • Marine data is only meaningful for coastal coordinates. For landlocked cities Open-Meteo returns empty arrays. The Marine tab had to detect this case gracefully — it hides itself rather than rendering an empty state, because an empty state implies the data should be there.
  • TanStack Start's server functions ran in a different execution context during development than production builds, which caused two different base URLs to be used for the same API call. Fixed by centralizing the base URL into a single server-side environment variable rather than letting each function construct its own.
  • Recharts' default tooltip behavior re-renders on every mouse-move over the chart, which was visible as jank on the hourly forecast line. Wrapped the tooltip content in React.memo and moved the data transformation out of the render function into a useMemo call — both changes together brought re-renders down to the chart library's own batching cadence.