Fetching latest headlinesโ€ฆ
What Did Earth Look Like When You Were Born? I Built It to Find Out
NORTH AMERICA
๐Ÿ‡บ๐Ÿ‡ธ United Statesโ€ขApril 19, 2026

What Did Earth Look Like When You Were Born? I Built It to Find Out

0 views0 likes0 comments
Originally published byDev.to

๐ŸŒ I Built a Climate Time Machine That Makes COโ‚‚ Data Feel Personal

This is a submission for Weekend Challenge: Earth Day Edition

The problem with climate data is that it's abstract. "+1.25ยฐC" doesn't land. "427 ppm COโ‚‚" doesn't hurt. Numbers don't move people โ€” stories do.

So I asked a different question: what if you could see exactly what the planet looked like the year you were born?

That's Earth Time Machine. Enter your birth year. Feel what changed.

What I Built

Earth Time Machine is an interactive climate data experience that makes global warming feel personally relevant by anchoring every metric to your birth year.

The core idea

Instead of showing abstract global averages, it asks: what was COโ‚‚ when you were born? How much Arctic ice existed? How many more people share this planet now? Every number becomes a story about your lifetime.

Features

  • ๐ŸŒ Animated 3D globe in the hero โ€” hand-coded in Canvas 2D, no WebGL library. The globe visibly browns and cracks based on the COโ‚‚ rise since your birth year. Born in 1960? Healthy green oceans. 2008? Noticeably warmer.

  • ๐Ÿ’จ COโ‚‚ breathing animation โ€” the number counts up from zero to your lifetime rise, then locks and pulses gently like Earth exhaling. No looping. It arrives and stays.

  • ๐Ÿ“Š 6 planetary vital sign cards โ€” COโ‚‚, temperature, Arctic sea ice, forest cover, population, sea level. Each has a sparkline, animated thermometers, comparison bars, and a "sting" โ€” a one-line gut-punch fact that reveals after you've absorbed the numbers.

  • ๐ŸŒ 20-country local data โ€” select your country and see local warming vs world average, per-capita COโ‚‚ then vs now, and your country's climate risk tier. India's local warming hits differently than Canada's.

  • ๐Ÿ”ฎ 4 IPCC scenario projections to 2100 โ€” actual path, Paris 1.5ยฐC, moderate action, worst case. Canvas-drawn chart showing where each path leads. Interactive tabs with verdicts.

  • ๐ŸŒฟ Paris Path toggle โ€” overlays what values should be if the Paris Agreement was met. The gap is sobering.

  • ๐ŸŽฎ Generational compare mode โ€” enter two birth years, see the COโ‚‚ absorbed between them, the temperature difference, the ice lost. Perfect for showing a parent vs child what Earth each of them inherited.

  • ๐ŸŽฏ Climate knowledge quiz โ€” 4 randomised questions from a pool of 8. Immediate feedback, score bar, explanations. Keeps people engaged after the data shock.

  • ๐Ÿ”Š Ambient sound โ€” two sine-wave oscillators through the Web Audio API. As you drag the year scrubber, the pitch shifts โ€” deeper in early decades, eerier as COโ‚‚ rises. Subtle but effective.

  • ๐Ÿ“ฅ Download your Earth Identity Card as PNG via html2canvas. Share your personal climate report on social media.

  • ๐ŸŒ Live COโ‚‚ clock in the sticky toolbar โ€” ticks upward in real time at the actual rate (2.4 ppm/year). Watch it move.

Demo

๐Ÿ”— https://earth-time-machine.vercel.app/

Try entering:

  • 1960 โ€” see a relatively healthy planet, then watch how much changed
  • 1990 โ€” the year of the Earth Summit, Rio. COโ‚‚ was already rising fast
  • 2005 โ€” the year Arctic ice hit a then-record low

Drag the year scrubber slowly from 1950 to 2026. Watch the COโ‚‚ pill change. Listen to the drone shift. Find 2007 โ€” the year the Arctic shattered its record.

Code

React + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

React Compiler

The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see this documentation.

Expanding the ESLint configuration

If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the TS template for information on how to integrate TypeScript and typescript-eslint in your project.




Built with:

  • React 19 + Vite 6
  • Tailwind CSS v4 (single @import "tailwindcss" โ€” no config file)
  • Canvas 2D for the globe and all charts (no chart library)
  • Web Audio API for ambient sound
  • html2canvas for PNG card export

Zero external data dependencies. All climate datasets (COโ‚‚, TEMP, ICE, POP, FOREST, SEA) are bundled from NOAA, NASA GISTEMP, NSIDC, FAO, and UN World Population Prospects.

How I Built It

The globe

The hardest part was making the hero globe look like Earth โ€” not a red blob, not random ellipses.

The breakthrough was switching from position: absolute painted ellipses to actual spherical projection math. Each continent is defined as a series of [lat, lon] pairs. A project(lat, lon, t) function maps them onto the 2D canvas using:

function project(lat, lon, t) {
  const phi    = (lat * Math.PI) / 180;
  const lam    = (lon * Math.PI) / 180 + t * 0.35; // t = spin time
  const cosLat = Math.cos(phi);
  const x3d    = cosLat * Math.sin(lam);
  const y3d    = Math.sin(phi);
  const z3d    = cosLat * Math.cos(lam);
  if (z3d < -0.05) return null; // back-face cull
  return [cx + x3d * r, cy - y3d * r];
}

Back-face culling (z3d < -0.05) means continents on the far side of the globe simply don't draw. The spin is smooth because t increments 0.008 per frame via requestAnimationFrame.

The heat factor maps COโ‚‚ rise since your birth year onto a 0 โ†’ 0.58 value that shifts:

  • Ocean colour (vivid blue โ†’ murky)
  • Land colour (forest green โ†’ reddish-brown)
  • Ice cap size (large โ†’ tiny)
  • Atmosphere halo (blue-cyan โ†’ dimmer)
let heat = 0;
if (birthYear > 0) {
  const rise = interp(CO2, 2026) - interp(CO2, birthYear);
  heat = Math.min(rise / 90, 0.58);
}

The COโ‚‚ number

The original version looped through seasonal offsets forever, making the number tick up and down endlessly. That felt anxious and wrong.

The fix: count up once with a cubic ease-out, then lock the value and add a pure CSS pulse โ€” no more JS interval touching the text:

const tick = (now) => {
  const p    = Math.min((now - start) / 1800, 1);
  const ease = 1 - Math.pow(1 - p, 3);
  el.textContent = `+${(target * ease).toFixed(1)}`;
  if (p < 1) requestAnimationFrame(tick);
  else {
    el.textContent = `+${target.toFixed(1)}`; // locked
    el.classList.add('breathing');             // CSS pulse only
  }
};
@keyframes co2Breathe {
  0%, 100% { transform: scale(1);      filter: drop-shadow(0 0 0px rgba(192,64,48,0)); }
  50%       { transform: scale(1.028); filter: drop-shadow(0 0 18px rgba(192,64,48,.45)); }
}

Gentle, readable, and the number never changes again.

Sound design

Web Audio API is blocked until a user gesture (browser policy). The fix is audioCtx.resume() which returns a Promise โ€” you must wait for it:

const toggle = () => {
  ctx.resume().then(() => {
    gain.gain.cancelScheduledValues(ctx.currentTime);
    gain.gain.setTargetAtTime(0.6, ctx.currentTime, 0.4);
    updatePitch(CO2[2026]);
  });
};

Two oscillators detuned by 2.5Hz create a warm beating effect. As the year scrubber moves, setTargetAtTime glides the frequency smoothly rather than jumping:

function updatePitch(co2) {
  const t    = Math.max(0, Math.min((co2 - 310) / 120, 1));
  const freq = 45 + t * 30; // 45Hz clean โ†’ 75Hz tense
  osc1.frequency.setTargetAtTime(freq, ctx.currentTime, 1.5);
  osc2.frequency.setTargetAtTime(freq * 1.045, ctx.currentTime, 1.5);
}

Architecture

The whole app is structured around a single truth: birthYear. Everything derives from it.

App.jsx
โ”œโ”€โ”€ birthYear (state) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”œโ”€โ”€ Hero           โ†’ onReveal(year) sets birthYear  โ”‚
โ”œโ”€โ”€ ShockSection   โ† birthYear                      โ”‚
โ”œโ”€โ”€ Toolbar        โ† country, toggles               โ”‚
โ”œโ”€โ”€ CountryPanel   โ† birthYear + country            โ”‚
โ”œโ”€โ”€ Scrubber       โ† birthYear + sliderYear         โ”‚
โ”œโ”€โ”€ ScenarioChart  โ† birthYear + scenario           โ”‚
โ”œโ”€โ”€ MetricCards    โ† birthYear + country + modes    โ”‚
โ”œโ”€โ”€ CompareSection โ† birthYear                      โ”‚
โ”œโ”€โ”€ Quiz           โ† birthYear (resets questions)   โ”‚
โ”œโ”€โ”€ Timeline       โ† birthYear (filters events)     โ”‚
โ””โ”€โ”€ ShareCard      โ† birthYear + country            โ”‚
                   โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

No Redux, no Zustand. Just useState in App.jsx with props passed down. The data layer is pure JS objects โ€” no API calls, no loading states, instant renders.

The data

All datasets are annual means from primary sources, hand-curated and interpolated:

// Linear interpolation across sparse data
export function interp(data, year) {
  if (data[year] !== undefined) return data[year];
  const keys = Object.keys(data).map(Number).sort((a, b) => a - b);
  // find surrounding years and lerp
  const t = (year - lo) / (hi - lo);
  return data[lo] + t * (data[hi] - data[lo]);
}

This means every year from 1950โ€“2026 returns an accurate value even if we only store data every 5 years.

What I'd do with more time

  • SVG world map with country-level temperature choropleth
  • Real Keeling Curve seasonal animation (actual monthly NOAA data)
  • Share to Twitter/X with OpenGraph preview card
  • Offline PWA support โ€” the whole app works without internet anyway

Design Philosophy

The biggest design decision wasn't technical โ€” it was emotional.

Climate data is usually presented as a problem to solve. Numbers, charts, policy. It lands as guilt or numbness.

Earth Time Machine tries a different frame: this happened in your lifetime. You were there. The COโ‚‚ that rose, rose while you were alive. The ice that melted, melted while you watched.

That's not guilt โ€” it's stakes. And stakes make people want to act.

The dark brown/forest green colour palette is intentional. Earth tones. Soil. The feeling of something organic under stress, not cold data on white.

The COโ‚‚ number that arrives and pulses โ€” not loops โ€” is intentional. It's not a process. It's a fact. It stays.

Prize Categories

Not submitting to sponsored prize categories โ€” this project uses no third-party AI APIs, authentication services, or blockchain infrastructure. Just the web platform, open climate data, and a weekend.

Data sources: NOAA Mauna Loa (COโ‚‚), NASA GISTEMP v4 (temperature), NSIDC Sea Ice Index (Arctic ice), FAO FRA 2025 (forest), UN World Population Prospects (population), CSIRO/Church & White 2011 (sea level). All datasets are annual means verified against primary sources.

Built over one weekend for Earth Day 2026. The planet deserved better tooling.

Comments (0)

Sign in to join the discussion

Be the first to comment!