A browser backtest can be serious work. It can also be a very fast way to put a polished interface around leakage. The runtime is not the standard. The replay contract is. Whether the model runs in TensorFlow.js, Python, or a spreadsheet, the test is honest only when every prediction uses information that existed before the event and is graded against a defined market reference afterward.
The browser adds useful constraints. Data can remain on the device, the full pipeline can be packaged with the page, and a reader can rerun the same workflow without provisioning a server. None of those benefits is automatic. “Client side” is an architecture claim that must be verified, not a privacy slogan printed above a loading bar.
Start with the backtest contract
Write the contract before loading a tensor. Name the prediction target, event boundary, feature-availability rule, training window, validation window, final test window, market reference, grading policy, and random-seed policy. If one of those fields is undefined, the model is not ready to train.
The target must match the betting question. A margin model, a win-probability model, and a player-stat model produce different outputs and need different baselines. Do not train one target and grade another because the second metric looks friendlier.
The event boundary is the line the data may not cross. For a pregame model, every feature needs an “available at” timestamp earlier than the decision. Final scores, closing prices, postgame summaries, and season aggregates that include the target event all belong on the forbidden side.
Errors should name the cure
A useful browser workflow returns boundary failures as values. “Feature available after event start — remove the column or rebuild it from lagged rows” is actionable. “Training failed” is not. The same applies to missing closes, duplicate events, mixed market types, and impossible probability values.
Do not silently drop bad rows. Report how many were excluded and why. A backtest that changes its sample while hiding the exclusions cannot be reproduced, even if the model code is deterministic.
Load data with provenance attached
A file name is not provenance. The dataset should identify its source, extraction time, transformation version, row key, and time semantics. The browser may receive the data from a static asset, a download, local storage, or an application endpoint. Whatever the path, the model card should name it.
Keep raw fields separate from derived fields. A rolling statistic should document the rows it can see and the lag applied before the target event. A market feature should document the book or consensus rule and capture time. An injury feature should document the report timestamp. These details matter more than the choice of activation function.
Before training, run simple audits: event keys are unique, timestamps are ordered, labels are absent from the feature matrix, future rows cannot enter rolling windows, and missing values are handled by a declared rule. The cheapest leakage test is often reading the columns out loud and asking when each one became knowable.
Split forward, never across the future
Random shuffling is natural for many machine-learning tasks and dangerous for sports replay. Adjacent games share teams, coaching systems, injuries, and market regimes. Mixing later events into training lets the model learn from a future that the live bettor did not have.
Use an expanding or rolling time split. Train on an earlier window, tune on the next untouched window, and reserve the final window for one evaluation after the design is settled. If you keep editing features after reading the final window, it is no longer final. Promote a newer untouched window or admit that the result is exploratory.
Hyperparameter search belongs inside the training and validation loop. The browser can automate that loop, but it cannot make repeated peeking harmless. Every choice influenced by the held-out result spends information from that result.
Compare against the market on the same rows
A model metric without a baseline is hard to interpret. For probabilistic predictions, compare the model with the devigged closing market on the exact same events. For ATS grading, publish wins, losses, win rate, window, sample, grading rule, and Do not turn the result into a return claim unless actual available prices and stake rules are part of the ledger.
The closing market is not perfect truth. It is a strong, timestamped competitor. Beating a weak naive baseline while losing to the close is not a betting edge. Beating the close on a design window is not proof either; the result still has to survive the untouched test and then live pregame use.
Keep prediction quality and ticket selection separate. A model may forecast every event but recommend action only when a defined condition is met. Grade the forecasts on all eligible rows and grade the selected tickets on the declared subset. Dropping low-confidence forecasts from the metric after seeing outcomes is selection bias.
Make browser execution reproducible
Record the dataset version, code version, model configuration, feature list, split boundaries, seed policy, runtime version, and evaluation output. A saved model without those fields is an artifact, not a reproducible experiment.
Exact binary equality across devices may not be realistic when hardware backends and floating-point kernels differ. Reproducibility should therefore distinguish identical inputs and configuration from acceptable numerical tolerance. Claim bit-for-bit output only after testing it across the supported runtimes.
Local storage can keep model state on the device, but the page may still fetch data or telemetry. Verify the browser's network panel, document what leaves the tab, and make publication a separate explicit action. “No upload” should describe observed behavior under the shipped configuration.
Stress the model before trusting the clean chart
Run a shuffled-label control. Performance should collapse toward the appropriate no-signal baseline. Remove the market feature and see what remains. Shift the test window. Split by season, team, favorite status, and market source when the sample supports it. Large swings are not automatically defects, but they are reasons to narrow the claim.
Audit calibration as well as rank order. A model that sorts events correctly but speaks in inflated probabilities can still recommend oversized or unjustified decisions. the calibration guide explains the reliability check; the Brier guide explains one proper scoring rule.
Keep the ugly runs. Removing a bad interval because “the league changed” requires a predeclared regime rule and a new test, not an edit to the chart after the fact.
Move from backtest to live shadow mode
The first production state should be observation, not action. Generate predictions before events, lock them with timestamps, save the available market, and grade after settlement. This creates a live-pregame sample that cannot benefit from backfill.
Only then compare the live behavior with the backtest. Differences can come from data latency, changed feature definitions, market-source drift, or an environment the training window did not represent. The right response is diagnosis, not a looser threshold.
Studio is the relevant modeling surface, the picks page is where live decisions can be inspected, and Workshop is the adjacent build context already linked by this article. Treat any interface claim as something the shipped product must demonstrate.
The watchpoint
A browser backtest earns trust when another person can identify the data, rerun the time-ordered split, reproduce the metric within the declared tolerance, and see every exclusion. The runtime makes that workflow portable. The evidence makes it real.




