npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@verifyhash/uv-index

v0.1.0

Published

Zero-dependency, network-free clear-sky UV-index estimator: solar-elevation-driven UVI, WHO exposure category, and burn-time guidance from lat/lon/date/time (+ optional ozone and elevation).

Downloads

32

Readme

uv-index

A zero-dependency, network-free clear-sky UV-index estimator for Node.js.

Give it a place (latitude / longitude), a date and time, and — optionally — the total-column ozone and site elevation. It computes the sun's elevation angle and derives an approximate clear-sky erythemal UV index (UVI), the matching WHO/WMO exposure category (Low / Moderate / High / Very High / Extreme), and a rough burn-time for fair skin.

Everything runs locally with the Node standard library. No network calls, no npm dependencies, no API keys.

Who it's for

Developers building sun-safety, weather, or outdoor-planning features who want a fast, offline estimate of "how strong will the sun be here, right now, if the sky is clear?" — for example:

  • a hiking / beach / gardening app that shades a timeline by UV strength,
  • a "reapply sunscreen" nudge that scales with the clear-sky UV ceiling,
  • a backfill for historical or future dates where no measured UVI feed exists,
  • a sanity bound to cross-check a measured/forecast UVI feed against.

It is a planning-grade ceiling, not a sensor. If you need the actual UVI right now, use a measured feed (they capture the clouds and haze this model deliberately ignores).

Install / use

No install step — it is a single self-contained module. Copy the uv-index/ folder into your project and require it:

const { estimateUVIndex } = require('./uv-index');

const r = estimateUVIndex({
  lat: 23.44,           // + North
  lon: 0,               // + East
  date: new Date('2023-06-21T12:00:00Z'), // a full UTC instant (date + time)
  ozoneDU: 300,         // optional, total-column ozone in Dobson Units (default 300)
  elevationM: 0,        // optional, site elevation in metres (default 0)
  skinType: 'II',       // optional, Fitzpatrick I–VI for burn time (default 'II')
});

console.log(r);
// {
//   uvi: 12.499,               // continuous clear-sky UV index
//   uviRounded: 12,            // rounded, as UVI is officially reported
//   category: 'Extreme',       // WHO/WMO band
//   solarElevationDeg: 89.6,   // sun altitude above the horizon
//   sunAboveHorizon: true,
//   burnTimeMinutes: 13,       // ~minutes of clear-sky sun to one MED (skin II)
//   inputs: { lat: 23.44, lon: 0, ozoneDU: 300, elevationM: 0, skinType: 'II' }
// }

date is interpreted as a UTC instant (both date and time-of-day matter — this is an instantaneous estimate, not a daily one). To model a specific local clock time, convert it to UTC first.

Other exports

const {
  estimateUVIndex, // the main estimator (above)
  categoryFor,     // (uvi) -> { category, min, max }  — WHO band for a UVI value
  burnTimeMinutes, // (uvi, skinType='II') -> minutes to one MED, or null at UVI 0
  solarElevation,  // (lat, lon, Date) -> sun elevation in degrees (vendored math)
} = require('./uv-index');

How the number is built

  1. Solar elevation is computed with the NOAA solar-position algorithm (Meeus-derived), vendored into lib/solar.js — see "Vendored solar math" below. If the sun is at or below the horizon, UVI is exactly 0.

  2. Clear-sky UVI from the elevation h:

    μ0  = sin(h)                     # = cos(solar zenith angle)
    UVI = 12.5 · μ0^2.42 · (Ω/300)^(-1.23) · (1 + 0.06 · z_km)
    • 12.5 is the base coefficient tuned so an overhead sun at 300 DU ozone and sea level peaks near the low-Extreme band, matching observed clear-sky maxima of ~12 at sea level.
    • 2.42 is a common empirical exponent for the erythemal (sunburn-weighted) UV response versus solar elevation.
    • Ω is total-column ozone in Dobson Units; the -1.23 exponent is the erythemal Radiation Amplification Factor (~1.2 fractional UV change per 1% ozone; literature values span ~1.1–1.4).
    • z_km is site elevation in km; +6% per km sits in the field-observed ~5–10%/km range.
  3. Category maps the UVI (rounded to the nearest whole number, as UVI is officially reported) to the WHO/WMO bands: 0–2 Low, 3–5 Moderate, 6–7 High, 8–10 Very High, 11+ Extreme.

  4. Burn time is the physical time to accumulate one Minimal Erythemal Dose (MED) for the chosen Fitzpatrick skin type: t = MED / (UVI · 0.025 W/m² · 60 s) minutes, using MED values of 200 / 250 / 350 / 450 / 600 / 1000 J/m² for skin types I–VI.

Honest limits — read this

This is a CLEAR-SKY model. It answers "what is the sun's UV ceiling under a cloudless, clean sky?" and nothing more:

  • No clouds. Real clouds can cut UVI by 50%+ (or, with bright broken cloud, briefly raise it slightly). This model never sees them, so on an overcast day its number will read too high.
  • No aerosols / haze / pollution. Urban or smoky air absorbs UV; the model ignores it and will overestimate in hazy conditions.
  • No surface albedo boost. Fresh snow (up to ~+90%), sand, and water reflect UV upward; the model does not add that, so it can read too low on snow.
  • The ozone and elevation terms are approximations. The 300 DU reference, the -1.23 ozone exponent, and the +6%/km altitude factor are single-value fits, not a radiative-transfer computation. Real ozone varies ~230–500 DU by latitude and season; if you don't pass a value the model assumes 300 DU.
  • Geometric sun angle, no refraction. Near sunrise/sunset (elevation within ~0.5° of the horizon) the true, refracted sun may be marginally higher than the geometric elevation used here; the UV contribution there is negligible anyway.
  • Mid-latitude clear-sky peaks read on the high side. Because there are no clouds or aerosols, a clear-sky summer noon at ~50° latitude lands near the top of the observed 7–9 window (this library returns ~9). That is the intended clear-sky ceiling, not a typical measured value.
  • Not medical advice. Burn time is an order-of-magnitude physical estimate; actual sunburn onset depends on sunscreen, reflection, medication, and individual skin. This is not a substitute for a measured UVI feed.

Use it for planning, shading UIs, and offline estimates — not for anything where being wrong by a few index points on a cloudy or hazy day matters.

Vendored solar math

The solar-elevation geometry in lib/solar.js is a trimmed copy of the sibling solar-calc project in this incubator (tools/solar-calc/index.js, v0.1.0 — the solarElevation / NOAA sunPosition path). It is copied in on purpose so uv-index has zero runtime dependencies and never networks to or requires another package. Both are MIT-licensed and share the NOAA Global Monitoring Laboratory Solar Calculator algorithm.

Running the tests

One command, no framework, no dependencies:

node test/index.test.js

The suite covers: sun below the horizon → UVI 0, a high-UVI low-latitude solar-noon case, a documented mid-latitude clear-sky band plus a winter contrast, WHO category boundaries, ozone/elevation correction direction, burn-time physics, and invalid-input rejection.

License

MIT — see LICENSE.