An interactive journey from the core of our planet to the edge of the observable universe. Explore. Discover. Understand.
Code
{// 1. CSS: Force high-contrast tooltips (readable in dark/light mode)const style =html`<style> g[aria-label="tip"] text { fill: #1a202c !important; font-family: sans-serif; } g[aria-label="tip"] path { fill: #ffffff !important; stroke: #e2e8f0 !important; } .plot-title { font-size: 20px; font-weight: bold; font-family: sans-serif; fill: #2d3748; } .plot-subtitle { font-size: 14px; font-family: sans-serif; fill: #4a5568; } </style>`;// 2. LIBRARIES & MAP DATAconst topojson =awaitrequire("topojson-client@3");let land =null;try {const world =awaitFileAttachment("world-110m.json").json(); land = topojson.feature(world, world.objects.land); } catch (e) {}// 3. ANNOTATIONS: Key locations to welcome students// These labels act as "tour guides" on the map.const annotations = [ {lon:-150,lat:40,label:"The Ring of Fire",rotate:-25}, {lon:-28,lat:20,label:"Mid-Atlantic Ridge",rotate:80}, {lon:90,lat:25,label:"Himalayan Belt",rotate:0}, {lon:38,lat:-5,label:"East African Rift",rotate:85} ];// 4. DATA FETCHING (Same robust logic as before)let earthQuakeData = [];try {const feed =await d3.json("https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_month.geojson"); earthQuakeData = feed.features.map(f => ({lon: f.geometry.coordinates[0],lat: f.geometry.coordinates[1],mag: f.properties.mag,place: f.properties.place,time:newDate(f.properties.time).toLocaleDateString(),type:"Earthquake" })); } catch (e) { earthQuakeData = [{lon:0,lat:0,mag:0,place:"Data Offline",type:"Earthquake"}]; }const volcanoes = [ {name:"Mt. Fuji",lat:35.36,lon:138.72,type:"Volcano"}, {name:"Krakatoa",lat:-6.10,lon:105.42,type:"Volcano"}, {name:"Mt. St. Helens",lat:46.19,lon:-122.19,type:"Volcano"}, {name:"Mauna Loa",lat:19.47,lon:-155.60,type:"Volcano"}, {name:"Vesuvius",lat:40.82,lon:14.42,type:"Volcano"}, {name:"Kilimanjaro",lat:-3.06,lon:37.35,type:"Volcano"}, {name:"Eyjafjallajökull",lat:63.63,lon:-19.62,type:"Volcano"}, {name:"Popocatépetl",lat:19.02,lon:-98.62,type:"Volcano"}, {name:"Etna",lat:37.75,lon:14.99,type:"Volcano"}, ];const allData = [...earthQuakeData,...volcanoes];// 5. RENDER WELCOME MAPreturnhtml`${style}${Plot.plot({title:"Welcome to a Living Planet",subtitle:"Every dot represents the Earth moving, breathing, and reshaping itself.",width:Math.min(900,window.innerWidth-40),height:520,projection:"equal-earth",color: {legend:true,domain: ["Earthquake","Volcano"],range: ["#f7ea3e","#f56565"] // Friendlier Blue and Red },marks: [// Ocean (Softer blue-grey) Plot.geo({type:"Sphere"}, {fill:"#f7fafc",stroke:"#cbd5e0"}),// Land land ? Plot.geo(land, {fill:"#2f6ba7",stroke:"white",strokeWidth:0.7}) :null,// Graticule (Subtle grid) Plot.graticule({stroke:"#cbd5e0",strokeOpacity:0.3}),// ANNOTATIONS: Large text labels on the map Plot.text(annotations, {x:"lon",y:"lat",text:"label",fill:"#718096",fontSize:14,fontStyle:"italic",fontWeight:"bold",stroke:"#f7fafc",strokeWidth:3,// White halo around text to make it readable }),// Data Points Plot.dot(allData, {x:"lon",y:"lat",r: d => d.type==="Volcano"?5:Math.max(1.8, d.mag*0.7),fill:"type",fillOpacity: d => d.type==="Volcano"?0.9:0.5,stroke:"white",strokeWidth:0.3,symbol: d => d.type==="Volcano"?"triangle":"circle",tip:true,channels: {Name: d => d.name|| d.place,Magnitude:"mag",Date:"time" } }) ] })}`;}
0.1 🚀 Your Journey Through 6 Units
Each unit is built around a driving question — a real-world mystery you’ll investigate using data, simulations, and scientific reasoning.
Code
{const units = [ {unit:1,title:"Discovering New Worlds",icon:"🌟",question:"How do we discover new worlds?",color:"#667eea",topics:"Sun's lifespan • Star life cycles • Exoplanets • Nuclear fusion"}, {unit:2,title:"Probability of Life",icon:"🧬",question:"What is the probability of life elsewhere?",color:"#00cec9",topics:"Big Bang • Early Earth • Water • Co-evolution"}, {unit:3,title:"Earthquakes & Volcanoes",icon:"🌋",question:"Who's at risk from earthquakes, volcanoes, tsunamis?",color:"#e17055",topics:"Earth's interior • Plate tectonics • Seismic waves • Hazards"}, {unit:4,title:"Climate Change",icon:"🌡️",question:"How is our climate changing?",color:"#6c5ce7",topics:"Earth-Sun dynamics • Carbon cycle • Feedback loops • Ice cores"}, {unit:5,title:"Hurricanes & Blizzards",icon:"🌀",question:"Will NYC see more hurricanes and blizzards?",color:"#0984e3",topics:"Severe weather • Storm paths • Climate impacts"}, {unit:6,title:"Sustainable Future",icon:"♻️",question:"How can we create a sustainable future?",color:"#00b894",topics:"Fossil fuels • Land use • Mining • Solutions"} ];const div = d3.create("div"); units.forEach(u => {const card = div.append("div").attr("class","unit-card").style("border-left-color", u.color); card.append("span").attr("class","unit-icon").text(u.icon); card.append("h3").style("color", u.color).text(`Unit ${u.unit}: ${u.title}`); card.append("p").style("font-size","1.1em").style("font-weight","600").style("font-style","italic").text(`"${u.question}"`); card.append("p").style("color","#718096").style("margin-bottom","0").text(u.topics); });return div.node();}
0.2 📊 What Makes This Textbook Different?
📊Interactive Graphs
Hover over any chart to explore data. Zoom, pan, and discover patterns yourself.
🗺️Live Maps
Visualize real earthquake, climate, and weather data on interactive maps.
🔬Simulations
Manipulate variables in orbital, climate, and geological simulations.
💻Runnable Code
See and modify the code behind every visualization.
✅Interactive Quizzes
Test your understanding with instant feedback after each chapter.
🌍Real Data
Work with the same datasets scientists use — from NASA, USGS, and NOAA.
0.3 🔥 A Taste of What’s Inside
Here’s a preview of the kind of data you’ll explore — global temperature change over the last 140+ years:
Code
{// 1. Generate Dataconst years = d3.range(1880,2025);const tempData = years.map(y => ({year: y,anomaly:-0.2+ (y -1880) *0.002+Math.sin((y -1880) *0.15) *0.1+ (y >1970? (y -1970) *0.018:0) + (Math.random() -0.5) *0.08,decade:Math.floor(y /10) *10+"s" }));// 2. Render Plotreturn Plot.plot({title:"🌡️ Global Temperature Anomaly (1880–2024)",subtitle:"Hover over any bar to see the year and temperature change",width:Math.min(800,window.innerWidth-60),height:350,x: {label:"Year",tickFormat:"d"},y: {label:"Temperature anomaly (°C)",grid:true},color: {scheme:"RdBu",reverse:true,// Reverses so Red = Hot, Blue = Coldlegend:true,label:"°C anomaly",// Optional: Set domain to be symmetric so 0 is perfectly whitedomain: [-1.2,1.2] },marks: [ Plot.ruleY([0], {stroke:"#a0aec0",strokeDasharray:"4,2"}), Plot.rectY(tempData, {x:"year",y:"anomaly",interval:1,// <--- THIS FIXED ITfill:"anomaly",tip:true,channels: {Year:"year","Temp Anomaly": d => d.anomaly.toFixed(2) +" °C" } }) ] });}
🧭 How to Navigate
Use the sidebar (left) to jump between units and chapters