Forecasting Pool Chemical Inventory from Logged Tank Levels
Chem Stock is a single-file, offline burn-rate forecaster for the chemical room. Log a tank or drum level whenever someone is in there; the tool derives the consumption rate from the history, projects the days remaining, and computes a reorder-by date against each supplier's lead time. Like the rest of the aquatics suite, it is one HTML file with no server and no external requests [1].
The problem
Chemical reordering at a pool tends to run on eyeball and memory: someone notices the chlorine tank looks low, an order goes in, and for the next several days the facility runs a race between the delivery truck and the tank. The failure is not that consumption is unknowable — it is very steady over weeks — it is that nobody is doing the division. Chem Stock does the division. Any level reading anyone bothers to log becomes signal, and the reorder date falls out of arithmetic instead of anxiety.
How it works
Each product shows as a card with a miniature tank fill and a status line: green (days left), amber (order by a named date), or red (ORDER NOW, with a hazard banner naming the product and its projected empty date). Selecting a product shows the detail view — a tank gauge drawn as SVG with a dashed red reorder line, the burn rate, the projected empty date, and the reorder-by date. Below that is the logging form (level plus an editable timestamp for backfilling) and the reading history.
The reorder line on the gauge is drawn at the level where remaining supply equals lead time plus buffer — when the fill drops to the dashed line, it is time to order. Copy status formats a one-line-per-product summary for pasting into DigiQuatics; Export CSV writes the full reading history (with delivery flags) as a UTF-8 file for budget season.
The math
Burn rate
The rate is computed from consecutive level drops over a trailing 45-day window. Walking each adjacent pair of readings:
- A level drop counts as consumption over that interval.
- A level rise greater than 5% of capacity is a delivery — the pair is excluded from the rate and the reading is flagged (▲) in the history.
- A smaller rise is measurement noise: the elapsed time counts, the consumption doesn't.
rate = total_consumed / total_days (over the trailing 45-day window)
days_to_empty = current_level / rate
reorder_by = projected_empty − lead_days − 3-day safety buffer
Status turns amber inside a 7-day window ahead of the reorder-by date and red once the date has passed.
Guards
The tool refuses to project until there is real signal. A single reading, same-day pairs, or a history entirely outside the trailing window all report logging — need more readings instead of a fabricated date. A level entry more than 15% over tank capacity is rejected at input as a probable typo.
Editing the config
Products live in the PRODUCTS array at the top of the <script> block — name, unit, tank capacity, and supplier lead days:
const PRODUCTS = [
{ id:"naocl", name:"Liquid Chlorine 12.5%", unit:"gal", capacity:500, leadDays:5 },
{ id:"ma", name:"Muriatic Acid 31.45%", unit:"gal", capacity:110, leadDays:5 },
{ id:"calhypo", name:"Cal-Hypo 65%", unit:"lb", capacity:400, leadDays:7 },
{ id:"cya", name:"Cyanuric Acid", unit:"lb", capacity:200, leadDays:7 },
];
The behavior constants sit directly below: SAFETY_DAYS (3), SOON_DAYS (7), WINDOW_DAYS (45), and NOISE_FRAC (0.05, the delivery-detection threshold). The capacities shipped in the file are placeholders — they need to be replaced with the facility's actual tank and drum sizes before the percentages and delivery detection mean anything.
Limitations
- The forecast is a trailing average. A heat wave, a swim meet, or a new leak changes the burn rate faster than a 45-day window notices — the projection catches up as new readings arrive, but it is a forecast, not a guarantee.
- Delivery detection is a threshold heuristic. A partial delivery smaller than 5% of capacity is counted as noise; logging a reading immediately before and after a delivery keeps the rate clean.
- History lives in one browser's
localStorage. It does not sync between devices; the CSV export is the backup and the transfer mechanism. - It forecasts stock, it does not order. The reorder-by date still has to become a purchase order by human hands.