The Inspiration

It always started with the same frustrating routine: checking a generic macro-forecast, throwing the board on my cafe racer, and riding down the coast to Lawrencetown Beach or The Moose, only to find the waves completely blown out or the swell missing the bay entirely.

Generic forecasting models simply do not understand local bathymetry. A 1.5-meter swell at 7 seconds creates a completely different ocean than a 1.5-meter swell at 14 seconds. I wanted to stop guessing and build a system that pulled raw, real-time offshore telemetry and ran it through a local physics engine to produce a simple, deterministic 1-to-5 score. If it says 5, you paddle out.

How It Was Built

SurfMatch is built as a cross-platform mobile application powered by a serverless Node.js backend deployed on Vercel.

The Data Pipeline: The backend is an orchestration engine that utilizes Promise.all to aggressively fetch and standardize real-time data from a global network of offshore buoys, including NOAA's NDBC, Australia's BOM, and Open-Meteo's marine models for hardware-sparse regions.

Monetization & RevenueCat: Because the app's core value proposition is saving surfers time and gas money, I built a premium subscription tier. Integrating RevenueCat was a game-changer for this sprint. It allowed me to instantly deploy paywalls, manage subscriber state, and link Apple Search Ads attribution directly to user LTV without having to write complex receipt validation code. RevenueCat handles the business logic so I could focus entirely on the forecasting engine.

AI-Powered Development with Google Gemini: Building a complex global forecasting engine as a solo founder requires serious leverage. I used Google Gemini as my pair-programmer throughout the entire build. From writing the JavaScript physics algorithms for the wave amplification model, to instantly debugging serverless Node.js syntax crashes in Vercel, to aggressively scaling our geographic JSON database to over 300+ global breaks, Gemini allowed me to ship a production-grade application in a fraction of the traditional time.

The Physics & The Scoring Algorithm

To translate chaotic telemetry into a clean 1–5 score, the forecasting engine models the relationship between deep-water energy and local shoreline bathymetry.

First, we establish the baseline wave energy flux, which is proportional to the square of the significant wave height $H_s$ and the peak period $T_p$:

$$P \propto H_s^2 \cdot T_p$$

But raw open-ocean energy doesn't guarantee rideable surf. The algorithm applies a directional penalization function based on the specific break's ideal swell window. If the incoming swell angle $\theta_{\text{actual}}$ deviates from the spot's ideal angle $\theta_{\text{ideal}}$, the energy is degraded linearly across a defined shoulder threshold $\Delta\theta_{\text{shoulder}}$:

$$W_{\text{dir}} = \max\left(0, 1 - \frac{|\theta_{\text{actual}} - \theta_{\text{ideal}}|}{\Delta\theta_{\text{shoulder}}}\right)$$

For breaks with extreme underwater irregularities—like deep-water canyons (e.g., Nazaré) or focusing reefs that drastically amplify the wave face—the engine applies a custom amplification multiplier $A_{\max}$. This scales down exponentially as the swell direction moves away from the canyon's optimal focusing angle $\theta_{\text{peak}}$:

$$H_{\text{amp}} = H_s \left( 1 + (A_{\max} - 1) \cos^2\left(\frac{\pi(\theta_{\text{actual}} - \theta_{\text{peak}})}{2 \Delta\theta}\right) \right)$$

The Challenges

The ocean is volatile, and so are government APIs.

  • Data Ingestion Resilience: NOAA's NDBC is notorious for quietly rate-limiting or blocking requests from Vercel and AWS data centers to prevent server abuse. To keep the pipeline alive, I had to spoof custom User-Agent headers and implement aggressive 9.5-second Promise.race fallback timeouts so a single unresponsive buoy network wouldn't crash the entire client payload.
  • Runtime Crashes: Transitioning the backend from Vercel's Edge runtime back to a standard Node.js serverless function caused fatal 500 FUNCTION_INVOCATION_FAILED errors due to Web API syntax conflicts. I had to completely rewrite the request handlers to use standard Express-style parsing while preserving the complex smart caching headers.

What I Learned

Shipping a complex data product is fundamentally an exercise in simplifying chaos. You have to handle thousands of lines of raw, unformatted text files in the backend so the user only ever has to look at a single green "Excellent" tag on their screen.

Taking the app on the road and field-testing the scoring system live at Mawi and Are Guling in Indonesia proved the model's global viability. The ocean has rules; you just have to know how to measure them.

Built With

Share this project:

Updates

Submission history