Tide and current prediction engine for TypeScript and Swift
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗Tide and current predictions. This repository holds the Slackwater engine for TypeScript, Swift and Kotlin, and the packages built on it. The engine predicts tides and currents in TypeScript, Swift and Kotlin; the CLI predicts both, and the React package shows tides and currents. Documentation lives at openwaters.io/tides/slackwater.
[!WARNING] Not for navigational use
Do not use calculations from this project for navigation, or depend on them in any situation where inaccuracies could result in harm to a person or property. Tide predictions are only as good as the harmonics data available, and these can be inconsistent and vary widely based on the accuracy of the source data and local conditions. The tide predictions do not factor events such as storm surge, wind waves, uplift, tsunamis, or sadly, climate change. 😢
Slackwater is one name for everything Open Waters publishes about tides and currents.
| Surface | Package | What it is |
|---|---|---|
| App | Slackwater for iPhone | Tide and current predictions on iPhone, offline (source) |
| Engine | slackwater | Tide and current predictions with the station database built in, for TypeScript |
@slackwater/engine | The harmonic tide and current engine for TypeScript, with no station data attached | |
| SlackwaterIWLS | Device-side CHS fetching with a shared contract and synthetic fixtures | |
| SlackwaterKit | The harmonic tide and current engine for Swift | |
| Slackwater for Kotlin | The harmonic tide and current engine for Kotlin/JVM and Android, consumed from source | |
| slackwater-iwls | Device-side CHS fetching for Kotlin/JVM and Android, on the same contract as SlackwaterIWLS | |
| CLI | @slackwater/cli | Tide and current predictions in your terminal: brew install openwatersio/tap/slackwater |
| API | @slackwater/api | HTTP API for tide and current predictions, hosted at api.openwaters.io/tides |
| React | @slackwater/react | React components for tide and current predictions |
| Database | @slackwater/database | The tide and current station database |
| Docs | openwaters.io/tides/slackwater | Developer documentation |
The name, logo, and colors are in brand.
Install the command line tool to get tide and current predictions from your terminal with Homebrew on macOS or Linux, or with npm (npm install -g @slackwater/cli).
brew install openwatersio/tap/slackwater
slackwater help # Show help and available commands
slackwater extremes --near 37.8,-122.5 # High/low tides near San Francisco
slackwater timeline --station noaa/9414290 # Water level timeline
slackwater stations "portland" # Search for stations
slackwater currents --near 48.406,-122.643 # Slack water and max currents at Deception Pass
slackwater currents stations "deception" # Search for current stations
slackwater serve # Start the REST API server for tides and currents
See the CLI README for full usage and installation options.
npm install slackwater
import { getExtremesPrediction } from "slackwater";
const prediction = await getExtremesPrediction({
latitude: 26.7, // or `lat`
longitude: -80.05, // or `lng` or `lon`
start: new Date("2025-12-17"),
end: new Date("2025-12-18"),
datum: "MLLW", // optional, defaults to station's chart datum (usually LAT or MLLW)
units: "meters", // optional, defaults to 'meters', can also be 'feet'
});
console.log(prediction);
// {
// datum: 'MLLW',
// units: 'meters',
// station: {
// id: '8723214',
// name: 'Fort Lauderdale, FL',
// // ...
// },
// distance: 12.3,
// extremes: [
// { time: 2019-01-01T03:12:00.000Z, level: 3.2, high: true, low: false, label: 'High' },
// { time: 2019-01-01T09:45:00.000Z, level: 0.5, high: false, low: true, label: 'Low' },
// ]
// }
import { getTimelinePrediction } from "slackwater";
const timeline = await getTimelinePrediction({
lat: 26.77,
lon: -80.05,
start: new Date("2025-12-19T00:00:00-05:00"),
end: new Date("2025-12-19T01:00:00-05:00"),
timeFidelity: 5 * 60, // seconds, defaults to `10 * 60`
units: "meters", // optional, defaults to 'meters', can also be 'feet'
});
console.log(timeline);
// {
// datum: 'MLLW',
// units: 'meters',
// station: {
// id: 'noaa/8722588',
// name: 'Port of Palm Beach',
// // ...
// },
// timeline: [
// { time: 2025-12-19T05:00:00.000Z, hour: 0, level: 0.7232836255650095 },
// { time: 2025-12-19T05:06:00.000Z, hour: 0.1, level: 0.7231644154595303 },
// { time: 2025-12-19T05:12:00.000Z, hour: 0.2, level: 0.7221097788490487 },
// { time: 2025-12-19T05:18:00.000Z, hour: 0.3, level: 0.7201242512580206 },
// // ...
// ]
// }
import { getWaterLevelAtTime } from "slackwater";
const prediction = await getWaterLevelAtTime({
lat: 26.77,
lon: -80.05,
time: new Date("2025-12-19T00:30:00-05:00"),
datum: "MSL",
units: "meters", // optional, defaults to 'meters', can also be 'feet'
});
console.log(prediction);
// {
// datum: 'MSL',
// units: 'meters',
// station: {
// id: 'noaa/8722588',
// name: 'Port of Palm Beach',
// // ...
// },
// time: 2025-12-19T05:30:00.000Z,
// hour: 0,
// level: -0.43840585181640557
// }
Slackwater uses @slackwater/database to find station data. You can find stations by location or ID.
import { nearestStation } from "slackwater";
const station = await nearestStation({ lat: 26.7, lon: -80.05 });
console.log(`${station.name} (${station.source.id})`); // Fort Lauderdale, FL (8722588)
Once you've found a station, you can get predictions, timeline, or water level at a specific time for that station:
// Get extremes prediction for the nearest station
station.getExtremesPrediction({
start: new Date("2025-12-17"),
end: new Date("2025-12-18"),
});
// Get timeline prediction for the nearest station
station.getTimelinePrediction({
start: new Date("2025-12-19"),
end: new Date("2025-12-20"),
});
// Get timeline prediction for the nearest station
station.getWaterLevelAt({ time: new Date("2025-12-19T00:30:00-00:00") });
import { stationsNear } from "slackwater";
(await stationsNear({ latitude: 45.6, longitude: -122.7 }, 5)).forEach((s) => {
console.log(`${s.name} (${s.source.id}) - ${(s.distance / 1000).toFixed(2)} km away`);
});
// Vancouver (9440083) - 3.49 km away
// Portland Morrison Street Bridge (9439221) - 10.24 km away
// KNAPP(THORNES)LNDG, WILLOW BAR (9440171) - 16.34 km away
// Rocky Point (9439189) - 16.93 km away
// WASHOUGAL, COLUMBIA RIVER (9440047) - 24.89 km away
An ID marked redundant follows its accepted replacement when that record has a tide model. A commercially usable record never automatically resolves to a noncommercial replacement; direct lookups of noncommercial records remain available.
import { findStation } from "slackwater";
// Find station by Slackwater ID
await findStation("noaa/8443970"); // Boston
// Find station by source ID (e.g. NOAA)
await findStation("9440083"); // Vancouver
The database also carries tidal current stations. Current predictions are signed speeds in knots along the station's flood axis: positive is flood, negative is ebb, zero is slack.
import { getCurrentEventsPrediction, getCurrentTimelinePrediction, slackWindows } from "slackwater";
const options = {
latitude: 48.406, // Deception Pass, WA
longitude: -122.643,
start: new Date("2026-06-01T00:00:00Z"),
end: new Date("2026-06-02T00:00:00Z"),
};
// Slack, max flood, and max ebb events in time order. Flood and ebb events
// carry the station's direction in degrees true when it publishes one.
const { station, events } = await getCurrentEventsPrediction(options);
// { time: Date, speed: -5.2, kind: "maxEbb", direction: 281.5 }
// Signed speed, magnitude, and direction when known, sampled every 10 minutes.
const { timeline } = await getCurrentTimelinePrediction(options);
// The windows where |speed| stays under a threshold around a reversal.
const windows = slackWindows(timeline, 0.5);
// [{ start: Date, end: Date }, ...]
Timeline points include nonnegative magnitude in knots and optional direction in degrees true. NOAA reference stations with both axes use the bundled minor-axis constants for rotating magnitude and direction. Stations without usable minor data and subordinate stations use absolute signed speed and their published phase bearings; see current prediction.
Current stations can also be found with nearestCurrentStation, currentStationsNear, and findCurrentStation, mirroring the tide station functions. Subordinate current stations (time offsets and speed ratios against a reference station) are resolved automatically.
Lookups by position only return stations that can be predicted, and only a station's primary depth bin. findCurrentStation also finds the rest by id: secondary bins such as noaa/EPT0003@11, and stations that can't be predicted, whose prediction methods throw the reason. currentStationUnavailable(station) gives that reason up front: redistribution for Canadian Hydrographic Service stations, whose terms don't allow their predictions to be redistributed (get them from tides.gc.ca), or no-model for a station with no constituents or offsets. parseCurrentBin(id) splits a bin id into its station and bin number.
Slackwater is continuously validated against NOAA tidal predictions, comparing the time and height of predicted high and low tides for all NOAA tide stations.
As of Feb 2026:
Slackwater reproduces high and low tide timing with sub-minute accuracy and tide heights with millimeter-level accuracy compared to NOAA's tide predictions. Validation is automated and enforced in CI to prevent regressions as the model evolves.
Canadian Hydrographic Service ports have no redistributable constants, so clients fit them on device from CHS's own predictions with chsTideFit. benchmarks/chs-fit.ts scores that recipe monthly on 64 ports at held-out weeks 28 to 270 days out. On the run recorded in the validation record, fitted from the samples a device fetches, about half the ports met the accuracy bar at the first held-out week, and the median held-out RMSE was 5–8 cm. Most misses come from the seasonal mean level, which a 60-day fit cannot carry (#428).
CHS current gates are fitted the same way from CHS's speed and direction predictions with chsCurrentFit, over 60 or 210 days per gate. benchmarks/chs-current-fit.ts scores both windows on a year-round grid of held-out weeks against CHS's published slack and peak times. openwatersio/chs-current-validation runs it every month, pools a year of rows per gate, and publishes each gate's tier and bounds in its releases. On this repository's run under scorer version 1 with the fit ending October 1, 2026, 10 to 19 gates met the accuracy bar at 210 days depending on the week, and verdicts for gates near the bar change with the season.
The libraries in this repository (slackwater, @slackwater/engine, @slackwater/cli, @slackwater/api, and @slackwater/react) are MIT licensed, and so are SlackwaterKit (swift/LICENSE), SlackwaterIWLS (module license), the Kotlin engine and its IWLS client. The iOS app is GPL-3.0. The station database code is MIT, and its station data carries a license per station: CC BY 4.0 unless noted, NOAA stations in the public domain, and some stations CC BY-NC 4.0, marked commercial_use: false. Every station has an attribution string to include wherever you show its data, and the @slackwater/react components make it available through Data sources disclosures, map attribution controls, or a caption below the widget. Nothing in these libraries enforces commercial_use: false: a commercial host must leave those stations out, which today are TICON records under CC BY-NC 4.0.