nerdspice

Migraine Prediction Using the Weather

Published:
Updated:

I built a small web app that uses hourly atmospheric pressure forecasts to flag pressure drops that are likely to trigger migraines.

tl;dr: Try out my migraine predictor.

Problem & Goal

I get frequent migraines and noticed they occur around storms and heavy rain. Research shows that rapid changes in the atmospheric pressure are a common migraine trigger. This project tests to see if I can predict migraine risk based on local weather information.

Off topic but related, recently I started watching some videos by tsoding on YouTube where he creates “recreational programming” videos by completing small projects on an interesting topic.

This inspired me to create a small project to see if I can determine if there is a risk of migraine based on the weather forecast for a specific zip code.

My Approach

I kept planning intentionally minimal and focused on just answering the question at hand, “can I use the weather to predict migraine risk?”

Weather API

I quickly found Open-Meteo, a free, open-source, and public weather API. The API allows you to fetch the forecast for a given latitude and longitude. It also has the ability to provide hourly data which is particularly helpful for predicting migraines.

From Zip Code to Coordinates

The first issue I encountered was needing the latitude and longitude to use the Open-Meteo API. There is no option to request by zip code.

I found Zippopotam.us, a public API for retrieving data about a zip code.

Wrapping it Up

To tie everything together, I kept it simple:

  • A single HTML page
  • Milligram CSS for lightweight styling
  • Vanilla JavaScript to glue everything together

No frameworks, no build steps. Just enough to show something.

How it Works

  1. The user enters a zip code
  2. The app gets latitude and longitude from Zippopotam.us
  3. It grabs the hourly pressure data from Open-Meteo for the next 24 hours
  4. The script computes the largest drop over a window of time.
  5. If the largest drop exceeds a threshold, then the app shows a migraine warning otherwise it shows no risk. The largest drop information is also shown to the user.

The Algorithm

The core logic looks for the largest pressure decrease across a window of time. Calculating this proved to be pretty simple. Here’s a trimmed version of the code that does the analysis:

function analyzePressureDrop(forecast, windowHours, horizonHours = 24) {
    // Extract the times and pressures from the Open-Meteo response
	const times = forecast?.hourly?.time;
    const pressures = forecast?.hourly?.surface_pressure;

	// Make sure we actuall have data to work with
    if(!Array.isArray(times) || !Array.isArray(pressures) || times.length !== pressures.length) {
	throw new Error("Forecast missing hourly time/pressure data.");
    }

    const now = Date.now();
    const horizonMs = horizonHours * 3600_000;
    const windowMs = windowHours * 3600_000;

    // Keep only points from "now" to "now + horizon"
    const pts = [];
    for(let i = 0; i < times.length; i++) {
	const t = new Date(times[i]).getTime();
	const p = Number(pressures[i]);

	if(!Number.isFinite(t) || !Number.isFinite(p)) continue;
	
	// If the time is now or in the future, and within our window, add to the points array. We only care about points in our window.
	if(t >= now && t <= now + horizonMs) {
	    pts.push({t, p, idx: i});
	}
    }

    if(pts.length < windowHours + 1) {
	throw new Error("Not enough forecast points in the selected horizon.");
    }

    let best = { drop: -Infinity, from: null, to: null};

    for(let i = 0; i + windowHours < pts.length; i++) {
	const from = pts[i];
	const to = pts[i + windowHours];

	// Sanity check: ensure it's about the right time gap
	if(Math.abs((to.t - from.t) - windowMs) > 30 * 60_000) continue;

	const drop = from.p - to.p;
	
	// Replace with the highest drop
	if(drop > best.drop) best = {drop, from, to};
	
    }
	
    return best;
}

Findings (So Far)

Since I only built this recently, there is limited real-world usage. However:

  • The app did not flag today as high-risk
  • I also did not get a migraine today

That’s not meaningful evidence but a good start.

Of course false positives and false negatives are expected. Migraines are highly individual and everyone has different triggers. I also need to look into how different time zones may affect the code and adjust accordingly.

I plan to revisit this section over the next few weeks as I collect more observations and tweak the code. I’ll provide an update at some point.

Project Info

If you’d like to check it out, the source code is available on CodeBerg and there’s a live version of Migraine Predictor on my website.

This was a fun experiment and reminder that not every project needs to be “production ready” to constitute being worthwhile.