Pick your river
Where each run is in the system
Streamflow & Fish Detections Live
Select a river and click "Load Flow Data" to fetch live streamflow
Data from USGS National Water Information System (waterservices.usgs.gov)
Power tool: drive any dashboard chart to one river
Buttons jump to the full chart; the panels below render here, pre-set to your river: same JSONs, one source. Entry-gate heatmap and streamflow keep their own pickers.
Daily Passage: Bonneville Dam Live
Daily Passage: Lower Granite Dam Live
Annual Run Size by Dam
Daily Passage by Destination River New
This season, river by river
Where the run is going, every year since 2012
Reading the two views: the Bonneville and Granite views count the same fish at two dams. A river's Bonneville number is always larger than its Granite number because about three in ten Snake-bound fish never complete the climb. Snake river pages count at Bonneville until the run reaches Granite in October, then switch to the Granite scale. And the current-season bar counts from June 1, while the season curve below counts from January 1 and projects to December 31, so its running total and projected finish will both read higher than this bar. Same run, different clocks.
The season behind every river
Cumulative Run Timing: Bonneville
Cumulative Run Timing: Lower Granite
Run Timing by River New
Basin Destination by Year New
Other/Unknown is not a river: it is the part of the dam count we could not attribute. In the migration-year view it is near zero for completed years; the calendar-year view is not closed to the census and carries the unattributed share. MY2011 is not drawn: 63 tags cannot split 414,057 fish. Rivers entering the Columbia below Bonneville are excluded here: those fish never passed the dam being counted. Yakima uses the Prosser Dam census in completed years, because a count beats an estimate.
* Method note: totals scale to the dam census, matching how Idaho Fish and Game publish theirs, less a correction of about five percent at Bonneville for fish counted twice after falling back over the dam. Hatchery and wild are allocated separately and recombined using the fin-clip census at the dam. The current-season split is provisional: it rests on this season's tags, and it will move as the fall run and the next model run land.
Sub-Basin Destinations New
Ocean Age × Origin (IDFG Style) New
How ocean age is known: only for fish PIT-tagged as juveniles, where we know the year they went to sea. Ocean age = (return migration year − smolt migration year) − 1. A fish tagged as an adult has no smolt year, so its ocean age is unknowable and it is excluded. How origin is known: a juvenile tagged at a hatchery is hatchery; one tagged at a screw trap or in-river is wild. The bar heights come from the DART dam census; only the split between age and origin classes is estimated from tags.
Read the labels before trusting a bar. Each shows the PIT tags behind it. Every drawn year rests on 1,400 to 10,700 aged tags (2012–13: 8,944, about 26 fish per tag; the thinnest, 2021–22, 1,750); a dam-year under 500 is not drawn and the axis says which. Ages come from each tag's mark year, origin from its rear type. Only ladder detections count: array detections years later read as extra ocean winters and are excluded.
Returning Adults by Drainage Beta
Hatchery vs Wild: Bonneville
Steelhead = the total run and Wild Steelhead = the wild subset of that total, so hatchery = total − wild. (Adding the two columns double-counts every wild fish and inflates the run 28–40%; that error reached this site once before.) This is a census of every fish that passed, not an estimate.Hatchery vs Wild: Lower Granite
Ocean Age × Origin (IDFG Style)
Winters at sea × hatchery/wild. Identical to the Destinations-tab copy: one number, two homes. Current season: see the one-salt collapse note.
Adult Length Distribution
Adult Length vs Ocean Age New
Columbia Basin Migration Flows New
Per-River Migration Flows New
Entry-Gate Detections by River New
Migration Timing by Year: Dams New
PTAGIS Detection Sites: Map
How to Use This Dashboard
Every chart on this site is interactive. Here are the key things you can do:
Legend Controls:
- Single click a year or river in the legend to hide/show it
- Double-click a legend entry to isolate it; only that item will show. Double-click again to restore all.
Dropdowns & Buttons:
- Use dropdown menus (top-left of charts) to switch between views: dam totals, individual rivers, smoothing modes, etc.
- On the Run Timing chart, the dropdown lets you pick Bonneville (BON) or Lower Granite (LWG) scaling and filter to individual rivers. Single-river views color lines by year so you can compare across seasons.
Chart Interaction:
- Hover over any line or bar to see detailed data: fish counts, percentages, PIT tag info, expansion factors
- Click + drag to zoom into a region. Double-click to reset zoom.
- Use the camera icon in the top-right toolbar to download a chart as a PNG image
What Can You Learn?
- Dam Passage: Compare this year's daily fish counts to historical patterns. Is the run early, late, big, or small?
- Run Timing: Track cumulative progress: what % of the run has passed? How does each river compare?
- Destinations: Where are the fish going? Basin destination shows estimated returns per river system. The "expansion" number in the hover tells you how many real fish each PIT tag represents (e.g., 5x means each tagged fish represents ~5 total fish). It is the final estimate divided by the tag-weighted fish behind it, so it shows how far the census closure moved that river (1.0x means the weighted tags alone accounted for it). Each tag's own weight is one over its release group's tagging rate, from the release records. It is not the dam count divided by the tags.
- Fish Biology: Size distributions and ocean age help understand the population mix (1-ocean vs 2-ocean fish).
- Migration Flows: Sankey diagram traces fish from Bonneville through each dam to their final destination river. Width of each band = number of fish. Use the year dropdown to compare across years.
- Fish Biology: Click any fish dot on the growth scatter to open its full tag history on PTAGIS. Orange diamond markers are fish with a single adult measurement only (age is estimated from length).
Dam & Site Abbreviations:
Where the run goes: three ways of asking
Scoreboard: our Idaho split vs IDFG, year by year
The genetics series was removed on 29 August 2026. It was built from CRITFC's upper-basin genetic groups only, which leave out the lower Clearwater and lower Salmon, while our series counts the whole basin. Different denominators, so the comparison was not well posed and should not have been published. It returns when we can match the sub-basins on both sides. The one genetics series that does match, CRITFC's Upper Columbia group, stays on the tracer chart on the How we know tab. What this means →
About SteelheadTracker
SteelheadTracker is a passion project that tracks Columbia Basin steelhead migration using publicly available data from DART (Columbia Basin Research), PTAGIS (PIT Tag Information System), the Fish Passage Center (hatchery releases and smolt monitoring), USGS (water data), and NOAA (weather forecasts).
Why should you trust this?
Fair question. I'm a fisherman who wanted answers I couldn't get on the schedule I needed them. The data was all public: dam counts, PIT tag detections. But the answers were locked in reports that arrive years late. I've always appreciated the run-breakdown graphs Idaho Fish & Game publishes; they calibrated my expectations. But they came out sporadically, and were never up to date or the most current when I was trying to make decisions about a trip.
So it started small: watching detections at two antennas on the South Fork Clearwater and weeding out fish that pinged the same array twice in a day. It turns out, in an unpredictable twist, that scaling up from two detection sites to the entire Columbia system was longer and harder than I thought. Ten months, a lot of evenings, and a healthy number of documented mistakes later, it worked. It's finally at a version that's good enough, at least for me, and I'm more particular than most people about this kind of thing.
What started as a small script became, working side by side with state-of-the-art AI over many months, a roughly 40,000-line Python analysis pipeline: fifteen-plus years of detection histories for hundreds of thousands of individually PIT-tagged steelhead, merged with the daily counting-window census at every major dam, plus published tagging rates, hatchery release records, and run reconstructions from IDFG and Columbia Basin research programs.
And because "a big pile of code" is not by itself a reason to trust anything, the pipeline spends as much effort checking its numbers as producing them. One number is scored against Idaho Fish and Game's published figures, the Clearwater-versus-Salmon hatchery split; the Snake wild split we publish is IDFG's own figure and cannot be scored; and every dam total is checked against the census. Outside Idaho only the Klickitat has a published count to score against (the Yakama Nation's Lyle Falls estimate); the other rivers' pages say so. Where our numbers and the published ones disagree, the chart says so instead of hiding it. That testing has caught my own errors before they reached you more than once; the one time it didn't, the What's New section below tells that story honestly.
The goal is simple: the same public data that powers DART and PTAGIS, vetted, cross-referenced, ranked, and presented so you can actually read it.
Data Sources:
- DART: Dam passage counts (daily & annual)
- PTAGIS: PIT tag detection data (individual fish tracking)
- PTAGIS Detection Sites: PIT tag monitoring site locations and metadata
- Fish Passage Center: hatchery release totals by year and basin, the denominator behind every tagging rate, and the Smolt Monitoring Program daily collection counts at Lower Granite, the sampling frame for fish tagged at the dam
- USGS: Streamflow and water temperature
- NOAA: River flow forecasts
- Columbia Basin Dam Map: Overview of dam locations in the basin
How It Works:
A build script fetches the latest DART dam passage data and combines it with precomputed PIT tag analysis (basin destinations, growth, origin classification). Charts are rendered client-side using Plotly.js, with no server computation needed.
Methodology Notes:
- Destination and returning adult charts default to migration year (June 1 to May 31); the destination chart's dropdown also offers calendar-year views to line up with dam passage data
- H/W classification from DART uses the "Wild Steelhead" column reported by dam operators
- PIT-based H/W uses release site information to infer hatchery vs wild origin
- Basin destinations: a Clearwater or Salmon hatchery fish is assigned the basin it was released into, every other hatchery fish is placed by where it was seen again upstream, Idaho wild fish follow Idaho Fish and Game's published wild split, wild fish elsewhere share one census-derived fraction, and the season in progress is split from this season's tags at Bonneville, sent to the rivers those release groups actually reached in past years
- Charts marked "Beta" are under active development and may have known limitations
A Note on the Numbers:
Estimating how many steelhead are heading to each river is a genuinely hard problem. Only a fraction of the fish carry PIT tags, and those tags have to be expanded (multiplied by factors from 10× to 50× or more) to estimate basin destinations. Expansion factors vary by year and river, some rivers have very few tags to work with, and no antenna detects every fish. So instead of asking you to trust the method, this site publishes its error: every estimate is scored against independent published sources (Idaho Fish and Game's hatchery figures for the Clearwater and Salmon, their wild escapement by stock for the Snake, and the dam censuses; rivers outside Idaho have no published figure to score against), the misses are documented alongside the hits, and when something turns out wrong it gets said here, not buried. See How accurate is this? for exactly how far to trust each number. If you spot something that looks off, or you have expertise that could improve the methodology, I'd genuinely like to hear from you.
Contact & Support:
Built by Paul Van Valkenburg, paul.vanvalkenburg@gmail.com
If you find this project useful and want to support its development, whether that means buying me a beer, helping me take my wife out to dinner to repair the damage this site has done to my marriage, or just keeping the servers running, any contribution is appreciated. Donate via PayPal
Want to receive updates? Email me to join the mailing list.
What's New
August 24, 2026: the site learns to talk, and every river gets a page.
New today: dedicated river pages (browse all 16) with each river's run timing, forecast wedge, season totals, and real PIT-measured travel times ("Clearwater-bound adults take a median 20 days Bonneville→Granite"); a daily "What the data says today" plain-words briefing on the Run Timing tab; the timing wedge charts promoted to the landing view; and a River-shaped alternate layout on the Migration Flows diagram.
3 September 2026. Two audits acted on, and the physics enforced.
Two more independent reviews of the site, twelve readers in all, and the numbers they found were fixed the same day. Upper Columbia rivers no longer sum to fewer fish than the dams that counted them; the Grande Ronde no longer reads more fish at Granite than at Bonneville; the chart caps and the river pages read one file of projected finishes; Cottonwood Creek moved to the Grande Ronde, where it is; three transposed and duplicated rows left the tagging-rate table; and which rivers are held to a typical share is now decided on the model's raw output, not on a chart the decision had already smoothed. The phone page is built with the site from the same data. One rate that stocked five rivers from one hatchery, the Klickitat's, is being priced by the river its tags were released into rather than by the hatchery's total.
2 September 2026. The season in progress, the Washington tag rates, and an audit.
The current season is now split from this season's own PIT tags at Bonneville. Each release group is sent to the rivers its fish actually reached in past years, which matters because Lyons Ferry stock does not go home and barged smolts are filed under a Lower Columbia release site. A river with fewer than twenty tags so far holds its usual share for the date. Upstream dam counts are no longer used as caps on the current season, because they lag Bonneville by weeks and a count to date is a floor, not a ceiling. Before this the Snake read 86 percent of the run so far against tags saying 47 to 50.
- Tag rates for Washington and Oregon. The Fish Passage Center's release table records zero PIT tags for Skamania, Lyons Ferry, Irrigon and Wells in most years; PTAGIS holds ten to forty thousand a year for each. The rate is now the PTAGIS count over the FPC release total, and where both sources counted tags they agree to within three percent. Fish tagged at the Lower Granite bypass are expanded by FPC's daily collection count there. The Fish Passage Center is now a named source.
- The Methow's history comes down. The Methow and Okanogan had been allowed up to two times the Wells Dam count, which is the tolerance a check uses, not a measurement. Seventy percent of Methow and Okanogan fish tagged at Bonneville are later seen at Wells, so the cap is now that conversion.
- An audit. Five independent reviews of this build, one per page, found about seventy items, from a Yakima count of zero caused by a missing map entry to sentences describing a method the site no longer uses. The numbers are fixed in this build, the copy is being corrected, and a second audit follows before anything is published.
1 September 2026. The level, and two rivers that were four times too big.
August fixed which river the run goes to. This update fixes how many fish are in it, which turned out to be a separate problem hiding behind the first one.
- Ten thousand fish a year were being deleted. The pipeline checked that the rivers never added up to more than the dam counted, and never checked that they added up to it at all. Every year came out at 71 to 95 percent of the real run, with the remainder simply gone rather than marked unknown. The hatchery level now sits about 2 percent above Idaho's published figures instead of 17 percent low every single year.
- The Methow and the Tucannon were roughly four times too large. Both for the same reason: the step that decides which river a fish is bound for was counting raw tags rather than expanded fish, so a river that tags heavily looked like it received more fish. The Methow read 13 percent of the Columbia hatchery run in 2012 and now reads under 3. Two mislabelled detector codes also sent Tucannon fish to the Tucannon that had never been there, including one that is a trawl in the Columbia estuary, 200 miles downstream.
- Every river now fits through its own dam. A river's fish must pass the dams between the ocean and home, so its number cannot exceed those counts. That check now passes for every completed year; the season in progress is not capped, because upstream counts lag Bonneville by weeks. It did not pass before.
- A new check the site cannot fake. Every PIT tag seen at Bonneville, multiplied by how many fish its release group stands for, reconstructs the season total to within about 10 percent of the physical dam count, without being told the answer. That is run by hand after every model rebuild. If the expansion ever breaks, this catches it, and nothing else could.
- Three missing years of Idaho wild data recovered. Spawn years 2017 to 2019 had been marked unavailable for months. They were published all along, as a picture of a chart with no readable text behind it.
- Forecasting Lower Granite from Bonneville. Snake fish pass Bonneville two to four weeks before Granite, so Bonneville's count is a real head start. By mid September it forecasts the Granite season to about 12 percent, against 34 percent for assuming last year. This site previously told you to assume last year. That was wrong, and the forecast page now says so.
- Said plainly what the wild numbers are. Roughly a third of the run is wild, and our wild figures are weaker than our hatchery ones. Outside the Snake there is no river-specific wild estimate at all. The accuracy page now spells out exactly which rivers and years that affects.
August 2026: a big update, and an honest one.
This site ran without updates from late February to late August 2026. During that gap I found several real problems in my own numbers, and rather than patch quietly I rebuilt the validation from the ground up. Everything below is now checked against published sources before it ships: Idaho Fish and Game's own figures for the Idaho rivers, the Yakama Nation's counts for the Klickitat, and dam-by-dam censuses everywhere. Rivers with no published figure are checked only against the dam counts their fish must pass, and a hard gate blocks the site from deploying if the numbers fail.
Fixed in this update:
- Eight charts restored: the live build had silently switched to an older script and dropped them (including the calendar/migration-year toggles).
- Ocean age corrected: 1-salt fish were dramatically undercounted by a day-counting bug; ages now derive from migration years and are validated against length distributions.
- One number per quantity: the cumulative run-timing chart and the destinations chart could disagree about the same river by 5x. Both now read from a single source. The Dam Passage tab's daily split by destination is a different quantity, the share of each day's tags seen later at each river's entry arrays, and it does not match these totals; it is being rescaled to them.
- Hatchery and wild estimated separately: the PIT-tag sample is 93–99% hatchery while the actual run is 52–72% hatchery. Pooling them made every destination estimate quietly a hatchery estimate. Wild allocation now uses wild sources and the dam fin-clip census.
- New: sub-basin destinations: Lochsa, Selway, Lemhi, Pahsimeroi and 18 more tributaries, from the separate hatchery/wild allocation.
- Confidence intervals repaired: some intervals didn't contain their own estimate after a late rescaling step; whiskers now wrap every bar.
- Impossible destinations removed: basins that enter the Columbia below Bonneville can't be destinations for fish counted at Bonneville.
- 2011 shown as unresolved: only 63 usable tags exist for that migration year, which cannot support per-river estimates. The bar now says so instead of pretending.
- Two data clocks: dam counts update daily (automated); PIT detection data runs through August 27, 2026: it refreshes with each model rebuild, not nightly, and this line states the real date rather than pretending otherwise.
Known limitations, plainly:
- Returns to the Snake mainstem itself are not shown as their own river; they are folded into the Salmon group, as Idaho Fish and Game publish them. Mainstem estimates lean on a model prior, because very few tags resolve there, so treat them as indicative rather than measured.
- The current migration year is always partial until the run completes in May.
- Tag-expansion methodology is under active validation against IDFG's published numbers; expect refinements, and expect me to say so here when they land.
Site build v142n · September 5, 2026