The sea rises and falls twice a day, and a sailor has to plan around it. Is there enough water to cross a sandbar at 3 o'clock? Will the mast fit under a bridge? Should you leave at dawn or at noon to ride the current home? This page uses tide predictions to answer those three questions. Heights and depths switch between feet and meters with the toggle at the top; currents are in knots, one nautical mile (1852 m) per hour. Every number comes from one made-up port, so don't navigate by it.
Why tides happen: W10 · Tides →A tide table lists the times and heights of high water (HW, the top of each rise) and low water (LW, the bottom of each fall) for a port. The range is the difference between a high water and the next low water. Ranges grow at springs, around new and full moon, and shrink at neaps, around the quarter moons; W10 · Tides shows why. Between the listed times you need a way to estimate the height, and sailors have a quick one: the rule of twelfths. Here it is tested against a predicted tide for a hypothetical port, built from four of the cosine waves W10 calls constituents.
How to read this: the green curve is the height of the water above chart datum through one day. Labeled dots are the high and low waters a tide table would list. Over the rise or fall at the cursor, the amber line is the rule of twelfths and the gold dashes are a pure cosine joining the same low and high.
Try this: press First quarter, then Full moon, and watch the range double while the amber rule keeps hugging the green curve.
How to read this: the top panel stretches one rise (or fall) to fill the frame. Its duration is split into six equal "tidal hours", and the height is shown as a share of the range in twelfths. The bottom panel shows how far the rule and the cosine miss the predicted tide, in height.
Try this: slide to tidal hour 1 and compare the rule with the cosine, then step to the next fall and see the error flip sign.
A nautical chart can't print the depth for every moment, so it prints depths below a fixed low reference level called chart datum. Many charts use Lowest Astronomical Tide (LAT), the lowest level the predicted tide reaches over many years; others use an average low-water level. Datum is chosen low so that the water is almost never shallower than the chart says. The tide table then gives the height of tide: how far the water surface sits above that same datum.
A spot marked as drying (sand or mud that's above datum) is under water only when the height of tide beats its drying height. In this toy port, datum is the lowest level the four waves can reach together, so the predicted height never goes below zero.
Split the rise from LW to HW into six equal parts (a "tidal hour" is the rise's duration divided by six, usually a little over an hour). The water climbs slowly at first, fastest in the middle two hours, and slowly again near the top. That's what a cosine does, and the rule is a whole-number version of it.
| hour | rule | cosine | difference |
|---|
Shares of the range at the end of each tidal hour.
On a cosine the rule misses by at most 1.6% of the range at the hour marks. Real tides aren't pure cosines: the daily waves (K1, O1) make one rise longer than the next and tilt its shape, and at ports with double high waters or a long stand at the top the rule fails outright.
import math PER = {"M2": 12.4206012, "S2": 12.0, "K1": 23.9344697, "O1": 25.8193417} # hours AMP = {"M2": 1.20, "S2": 0.40, "K1": 0.10, "O1": 0.08} # meters, toy port PHA = {"M2": 60, "S2": 60, "K1": 100, "O1": 40} # degrees Z0 = sum(AMP.values()) # mean level above datum (datum = toy LAT) def height(t, age): # t: hours after midnight, age: days since new moon tau = 24*age + t return Z0 + sum(AMP[k]*math.cos(2*math.pi*tau/PER[k] - math.radians(PHA[k])) for k in PER) def extrema(age, step=1/60): # HW/LW where the rate of rise changes sign out, ts = [], [i*step for i in range(int(24/step) + 1)] for a, b, c in zip(ts, ts[1:], ts[2:]): ha, hb, hc = height(a, age), height(b, age), height(c, age) if hb > ha and hb >= hc: out.append(("HW", b, hb)) if hb < ha and hb <= hc: out.append(("LW", b, hb)) return out TWELFTHS = [0, 1, 3, 6, 9, 11, 12] if __name__ == "__main__": for k in range(7): cos_share = (1 - math.cos(math.pi*k/6))/2 print(k, TWELFTHS[k]/12, round(cos_share, 4), round(TWELFTHS[k]/12 - cos_share, 4)) for kind, t, h in extrema(age=1): print(kind, f"{int(t):02d}:{round(t%1*60):02d}", f"{h:.2f} m")
A boat's draft is how far its keel reaches below the waterline. The water left between the bottom of the keel and the seabed is the under-keel clearance. Over a sandbar it changes all day as the tide rises and falls, so the skipper looks for a window: the hours when the clearance is at least a chosen safety margin. Bridges work the other way round. The air draft is the height from the waterline to the top of the mast, and a bridge's clearance is printed above a high-water level, so the mast fits only when the tide is low enough. One channel can need high water for the bar and low water for the bridge.
How to read this: the green curve is the height of tide (the same toy port as Lab 1). Over the bar you're safe whenever the curve is above the amber line; under the bridge, whenever it's below the blue line. The shaded hours are those windows, and the strips at the top repeat them.
Try this: press Neaps and watch the bridge windows shrink, because the low waters stay high.
How to read this: a slice through the water at the bar's crest, seen from the side. The arrows on the right measure up from chart datum; the arrows at the keel measure down from the surface. A negative charted depth means the bar dries: it sits above datum.
Try this: drag charted depth below zero to make the bar dry, then find the hours you can still float across.
How to read this: the dashed line is the high-water reference (toy HAT) that the bridge's charted clearance is measured from. When the tide is below it, there's extra room. The gap between masthead and bridge is colored like the keel clearance above.
Try this: raise the air draft until there's no bridge window left, then lower the margin and see which hours come back.
Each condition is a single horizontal line on the tide curve, so a window opens and closes where the curve crosses that line. The page finds each crossing by scanning the day every 5 minutes and then halving the bracket until it's pinned to well under a second.
Depths are measured down from a low level and bridge clearances up from a high one, so most of the time the real water gives you more room than the chart. Bridge clearances are usually printed above a high-water level such as Highest Astronomical Tide or a mean high water; the chart's notes say which. A high-pressure system or an offshore wind can still hold the water below the prediction. A rule often quoted is that the sea drops about 13 inches for each 1 inHg rise in air pressure, and storm surge can raise it much more, as in W00 · Hurricanes. How deep the boat sits depends on its E07 · keel and ballast, and a fast boat in shallow water squats lower near its E06 · hull speed.
from lab1 import height, Z0 # the toy port above HAT = 2*Z0 # 3.56 m: highest the four waves can reach together def windows(f, t0=0, t1=24, step=5/60): """hours where f(t) >= 0: scan, then bisect each sign change""" out, a, fa = [], t0, f(t0) start = t0 if fa >= 0 else None while a < t1: b = min(t1, a + step); fb = f(b) if (fa >= 0) != (fb >= 0): lo, hi = a, b for _ in range(60): m = (lo + hi)/2 if (f(m) >= 0) == (fa >= 0): lo = m else: hi = m if fb >= 0: start = lo else: out.append((start, lo)); start = None a, fa = b, fb if start is not None: out.append((start, t1)) return out age, charted, draft, margin = 0, 0.6, 1.9, 0.3 clr, air, gap_margin = 13.0, 15.0, 0.5 bar = windows(lambda t: charted + height(t, age) - draft - margin) bridge = windows(lambda t: clr + (HAT - height(t, age)) - air - gap_margin) print("bar", [(round(a, 2), round(b, 2)) for a, b in bar]) print("bridge", [(round(a, 2), round(b, 2)) for a, b in bridge])
When the tide rises, water has to flow in from somewhere, and in narrow channels that flow becomes a current called the tidal stream. The flood runs one way while the tide rises and the ebb runs back while it falls. In between comes slack water, when the stream stops and turns. A boat's ground speed, its speed over the seabed, is its speed through the water plus the stream if the stream is behind it, or minus the stream if it's ahead. Sailors estimate the stream with the 50/90 rule: in the six hours of a flood or ebb, it runs at about half, nine-tenths, full, full, nine-tenths and half of its maximum rate. It's a rule of thumb, compared here with a sine curve.
How to read this: above zero the stream runs east (flood), below zero west (ebb). The amber steps are the 50/90 rule and the gold dashes are a sine with the same maximum; the one you pick drives the passage below. The green band is your passage.
Try this: switch between Springs and Neaps and read the peak rate off the chart.
How to read this: the boat sails the channel at a steady speed through the water. Blue arrows show the stream at the clock time. The amber ticks mark where the boat was after each hour; the dim ticks underneath are where it would be in still water.
Try this: press Best departure, then Sail it, and see the amber ticks spread out as the stream helps.
How to read this: each point on the green curve is one full passage, started at that time and stepped along the channel minute by minute. The dashed line is the still-water time. The lowest point is the best time to leave.
Try this: stretch the channel from 12 to 30 nm and watch the best departure move earlier, so the strongest fair stream lands in the middle of the trip.
The boat starts at A and adds up a minute of progress at a time, using the stream in the middle of each minute, until it covers the channel length. A step is cut short where a new tidal hour begins, so it never straddles one of the rule's jumps. The page repeats that for a departure every 10 minutes to draw the bottom chart. On a day between springs and neaps, Vmax is found by scaling between the neap and spring rates by the day's range, which is how tidal stream tables are usually read.
| hour | 50/90 | sine, hour average |
|---|
The rule gives the stream a flatter top than a sine. Added over a six-hour flood it moves 4.8 hours' worth of the maximum rate, while a sine with the same peak moves 12/π ≈ 3.82. Neither shape is right for every channel; a tidal stream atlas, or the stream tables printed on many charts (tidal diamonds on British Admiralty charts), give measured rates hour by hour. To steer across a stream instead of along it, see E08 · Navigation; for how wind angle sets the boat speed you feed in here, E05 · Sailing upwind.
import math M2 = 12.4206012 # hours: one flood + one ebb RULE = [0.5, 0.9, 1, 1, 0.9, 0.5] def stream(t, s0, vmax, mode="rule"): # knots, + = east-going flood p = ((t - s0)/M2) % 1 sign = 1 if p < 0.5 else -1 q = (p % 0.5)*12 # 0..6 hours into this flood or ebb f = RULE[min(5, int(q))] if mode == "rule" else math.sin(math.pi*q/6) return sign*vmax*f def passage(dep, L, vb, direction, s0, vmax, mode="rule", dt=1/60): x, t, hh = 0.0, dep, M2/12 while t < dep + 48: nb = s0 + (math.floor((t - s0)/hh + 1e-9) + 1)*hh # next tidal-hour boundary s = min(dt, max(nb - t, 1e-9)) # don't step across a jump v = max(0.0, vb + direction*stream(t + s/2, s0, vmax, mode)) if x + v*s >= L: return t + (L - x)/v - dep # hours x, t = x + v*s, t + s return math.inf s0, vmax = 8.28, 3.0 # springs: first LW 08:17, 3 kn times = [(d/6, passage(d/6, 12, 5, +1, s0, vmax)) for d in range(145)] best = min(T for d, T in times) ties = [round(d, 2) for d, T in times if T <= best + 1/60] # within a minute of the best print(f"best passage {best:.2f} h vs still water {12/5:.2f} h; leave at any of {ties}")