A browser model is not impressive because it runs in a browser. It is impressive only when the prediction can be reconstructed from information that existed before kickoff. TensorFlow.js gives you a convenient runtime, but it cannot protect you from stale features, a contaminated split, or a market quote collected after the decision.
The useful version of this tutorial starts before the network. Define the wager, define the timestamp, define the label, and make every missing input explicit. Then build the smallest model that can challenge a plain baseline. The old copy skipped that discipline and filled the gap with uncited benchmark scores, device timings, architecture folklore, and example games. Those claims are gone.
Write the prediction contract first
A spread model needs one stable point of view. Decide which team is positive, how pushes are represented, which market snapshot is the benchmark, and when the prediction is considered published. Store those choices with every row. A sign convention that changes between training and grading can make a model look wrong when the real defect is bookkeeping.
The feature row should contain only fields available at the declared cutoff. Team ratings, rest, projected availability, weather, and market context can all be useful, but each needs an observed time. A value corrected after the game cannot quietly replace the pregame value in the training table.
Keep unavailable and neutral separate. Missing quarterback status does not mean healthy. Missing weather does not mean calm. Return an error value with a cure such as “refresh the availability snapshot” or “exclude this event from training” rather than substituting a plausible default.
Begin with a baseline the network must beat
Before adding hidden layers, fit a transparent baseline on the same rows and the same time splits. A market-only baseline shows how much information is already in the quote. A compact linear model shows whether your engineered features carry anything beyond that quote. If the neural model cannot improve probability quality or calibration on untouched future games, keep the baseline.
This comparison catches more defects than another tuning pass. When every model appears brilliant, inspect the split. When every added feature helps, inspect the timestamps. When the network mirrors the market almost perfectly, check whether the closing line has leaked into a decision-time model.
Use the Brier score guide for probability scoring and the calibration guide for confidence. Neither metric should be read without the evaluation window and the number of graded predictions.
Choose an architecture for the data you have
Most spread datasets are small tables, not image corpora. A compact dense network is a candidate because it can learn interactions without demanding a huge artifact. That is not a license to add layers until the validation curve looks exciting. Capacity should grow only when an error analysis identifies structure the current model cannot express.
Put architecture settings in a typed configuration object. Name the feature count, activation, regularization, optimizer, stopping rule, and seed. The training function should return either a versioned artifact or a typed failure with a cure. A failed tensor conversion, an empty fold, or an incompatible feature schema must never fall through to a prediction.
Normalize numeric inputs with statistics learned from the training period only. Apply the same stored transform to validation, test, and live rows. Recomputing normalization on the full dataset lets the future influence the past even when the target column never appears in the features.
Split through time
Random shuffling is friendly to sports models because team strength, roster state, and market behavior repeat across nearby games. It is also the wrong test for a model that will face tomorrow. Train on earlier events, tune on a later block, and reserve a still-later block for the decision you intend to publish.
Do not keep peeking at the final block. Once a result changes your architecture, feature list, or stopping rule, that block has joined the development process. Advance the cutoff and create a new untouched evaluation period.
Save the prediction from every forward fold. Those out-of-fold rows support calibration, error review, and later stacking without asking a meta-model to learn from in-sample confidence.
Train with guardrails, not hope
Training should stop for a declared reason. Monitor validation loss, preserve the best checkpoint, and record the seed and feature schema beside the artifact. A run that ends because the tab closed is not a model version. A run that cannot be replayed is not evidence.
Add negative controls. Shuffle the labels and confirm the apparent signal disappears. Remove the market feature and see how much independent information remains. Train on the market feature alone and compare it with the richer model. These tests are designed to make the model fail for the right reasons.
Inspect prediction distributions as well. A network that emits almost the same probability for every game may be stable but useless. A network that lives at the extremes may be overconfident. Calibration should determine how much disagreement is actionable, not the drama of the raw output.
Evaluate the decision, not just the label
The target tells you whether a side covered. The market row tells you whether the decision was available. Keep the sportsbook, spread, attached price, capture time, and publication time together. Grade the original quote rather than a later number that flatters the pick.
Report a betting record only as wins, losses, pushes, ATS win rate, named window, and sample size. This module has no cited prediction ledger, so it publishes no record. It also makes no claim about closing movement, profitability, or a production model.
Bucket calibration by the probabilities the model actually issued, then examine the region where a card would be published. A good overall score can hide a broken decision region. The model earns deployment only when its confidence behaves sensibly where the product uses it.
Prove browser parity
Training and inference may use different backends. Export the artifact, reload it through the exact browser path, and send fixed feature rows through both runtimes. Compare outputs within a declared tolerance. Include missing fields, extreme values, and schema errors in the fixture.
Measure cold start and steady-state inference on supported devices rather than repeating generic speed claims. Record the browser, backend, device class, artifact version, and batch shape beside each observation. This article declares no such benchmark rows, so it names no timing or size.
Version the feature schema independently from the weights. A retrain on newer data is not the same change as adding a feature. The consumer should reject an incompatible artifact with a cure string rather than quietly rearranging columns.
Publish an honest empty state
A browser can finish training and still produce nothing worth betting. The honest result is “no deployable model from this evaluation window,” followed by the reason: no improvement over baseline, poor calibration, unstable seeds, stale inputs, or runtime mismatch.
Keep the experiment ledger in the same place as the artifact. The framework comparison explains when a table may favor boosted trees, while the tracking guide covers the receipt trail once a prediction becomes a published selection.
This module teaches the build order: data contract, baseline, time split, compact candidate, calibration, runtime parity, and only then publication. It does not certify a TensorFlow.js edge. That claim must come from a cited pregame table and a graded ATS ledger.
Model calibration from graded predictions
Calibration points render only when a verified source binds prediction probabilities to settled outcomes for the same observations.
Expected value from graded outcomes
Expected-value cells render only when a verified source binds observed win outcomes to the price paid for the same bets.




