Inspiration

Since our last ski/snowboard trip in December, we noticed skiers and snowboarders drive hours and spend hundreds of dollars on a trip without truly knowing what conditions await them. Weather apps give raw numbers like "8 inches of snow, 25 mph winds" that mean nothing to someone who hasn't experienced those conditions. We wanted to build something that lets riders see what they're heading into before they go. A blizzard at 12,000 feet looks and feels completely different from light powder at the base, and we wanted to show that difference visually, not numerically.

What it does

POWDAY is a real-time mountain weather visualization tool that translates weather data into an immersive 3D ski slope scene. Users explore ~28 major US ski resorts on an interactive globe (with Google Maps satellite and Three.js globe views), select a resort, and are transported into a first-person 3D scene showing actual conditions: snowfall intensity, rain, fog, wind, and surface type, all rendered in real time using live weather data from OpenWeather and Open-Meteo APIs. An elevation slider lets riders see how conditions change from base to summit, with the 3D scene smoothly transitioning between weather modes (bluebird, light snow, storm, blizzard, drizzle, rain) as they drag. A conditions overlay displays rider-relevant stats like feels-like temperature, fresh snow, wind, visibility, and snow type classification (fresh powder, packed, ice, slush, etc.).

How we built it

  • Frontend: React 19 with TanStack Start (file-based routing), MobX for state management, Tailwind CSS 4 with shadcn/ui components
  • 3D Globe: Dual-renderer architecture with Google Maps 3D tiles (satellite) and Three.js (stylized globe) using a Strategy pattern, switchable at runtime via a toggle
  • 3D Ski Scene: Custom Three.js simulator with GPU particle systems for snow and rain, PBR terrain with snow textures, GLTF pine trees and skier models, HDRI environment maps, and a first-person camera controller with ski tip parallax
  • Weather Integration: Multi-source data fusion using OpenWeather One Call API 3.0 for current conditions and Open-Meteo for elevation-aware forecasts, with automatic fallback if either source fails
  • Architecture: Clean MVVM pattern following Domain Models → Stores → Presenters → View Models → Components, with external API adapters behind interfaces
  • Database: Prisma with SQLite containing a curated dataset of 28 ski resorts with coordinates, elevations, and resort images

Challenges we ran into

  • Performance: The 3D simulator ran beautifully standalone but lagged severely when embedded in the React app. We discovered that 120,000 snow particles calling trigonometric functions every frame (180,000+ sin/cos calls per frame just for ground collision) was crushing the main thread. We solved this by creating a fast terrain height approximation, eliminating per-particle trig calls, and isolating the 3D renderer in an iframe with postMessage communication so it runs in its own JavaScript context
  • Race conditions: The scene page's elevation slider wouldn't appear because the resort data fetch hadn't completed when the presenter tried to find the resort. We used MobX's when() to await store loading and wrapped route components in observer() so they react to async state changes
  • Weather API reliability: OpenWeather API calls would fail and take down the entire scene page. We added try-catch fallback logic in all three use cases so Open-Meteo serves as a resilient backup and the app degrades gracefully rather than crashing
  • CSS backdrop-blur competing with WebGL: Four overlay elements using backdrop-blur-xl forced the browser to re-composite the entire WebGL framebuffer through a blur filter every frame, halving our frame rate. Switching to solid semi-transparent backgrounds eliminated the GPU compositing overhead

Accomplishments that we're proud of

  • The dual globe renderer that lets users switch between Google Maps satellite terrain and a stylized 3D globe with smooth fly-to animations on resort selection
  • The weather-to-scene mapping that translates real API data into convincing visual conditions. Watching a blizzard roll in as you drag the elevation slider up is genuinely immersive
  • The architecture discipline of maintaining clean separation of concerns (presenters, view models, stores, domain models) under hackathon time pressure, which actually saved us time when debugging the race conditions and performance issues
  • Using test-driven development to create fallbacks
  • Generated over 24,000 lines of code

What we learned

  • GPU particle systems and React don't share a thread well. The iframe isolation pattern was a key learning. Sometimes the right architecture isn't about making things work together, but about giving them separate execution contexts
  • Trig functions in hot loops are devastating. Replacing Math.sin/Math.cos with simple arithmetic in a 30k-iteration per-frame loop was the single biggest performance win, more impactful than halving particle counts
  • MobX observers re-render aggressively. Every observable change triggers the full component tree. React.memo, local state during drag interactions, and debounced API calls were essential to keep the UI responsive alongside 3D rendering
  • Design isn't just colors, it's feel. Our first "winter theme" was just pale blue. It took multiple iterations with frosted glass effects, icy atmosphere glow, cool-toned gradients, and proper contrast to make the app actually feel like snow sports

What's next for POWDAY

  • Historical playback: "What did conditions look like last Tuesday?" using NOAA historical data, rendered as a time-lapse in the 3D scene
  • Mobile-responsive globe with touch gestures for pinch-zoom and drag
  • Resort comparison mode to split-screen two resorts side by side and compare conditions
  • Snow type surface rendering with visually different terrain textures for powder vs. ice vs. crud, driven by the snow quality classification
  • WebGPU migration for the particle systems to unlock 10x more particles without performance cost
  • User accounts with saved favorites and trip planning alerts ("Notify me when Breckenridge gets 6+ inches of fresh powder")

Built With

Share this project:

Updates