Chronos-2 (120M), ONNX for the browser
An unofficial community conversion of Amazon's Chronos-2 time-series foundation model. It runs fully client-side with ONNX Runtime Web on WebGPU or WASM: no server, no Python, and the data stays in the browser.
Amazon does not make or endorse this conversion. For the original model, see amazon/chronos-2 and amazon-science/chronos-forecasting.
Live demo: webTimesFM runs this model next to Google's TimesFM 2.5, on the same data, with a "Compare both" mode.
Files
| File | Size | Description |
|---|---|---|
chronos2_q8.onnx |
153 MB | Recommended. 8-bit weights (MatMulNBits, symmetric, block 32) in the 12 encoder blocks; input and output projections kept at full precision. fp32 activations |
chronos2_q4.onnx |
96 MB | Smaller 4-bit build, same layout. Fine on long histories, less accurate on short series |
chronos2_fp32.onnx |
478 MB | Full precision, for checking and further conversion |
chronos2.js |
6 KB | JS wrapper: scaling (mean/std + arcsinh), patching, time encoding, un-scaling |
chronos2_selftest.json |
64 KB | 8 test contexts with forecasts from PyTorch Chronos2Pipeline (all 21 quantiles) |
scripts/ |
— | Export, quantisation and verification scripts used to make this |
Quick start
Download chronos2.js and put it next to your page, then:
<script type="module">
import * as ort from "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.29.0/dist/ort.webgpu.min.mjs";
import { Chronos2 } from "./chronos2.js";
ort.env.wasm.wasmPaths = "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.29.0/dist/";
const model = await Chronos2.load(ort,
"https://huggingface.co/VishalMysore/chronos-2-onnx-web/resolve/main/chronos2_q8.onnx",
navigator.gpu ? "webgpu" : "wasm");
const history = [/* your numbers, oldest first; NaN = missing */];
const { point, quantiles, levels } = await model.forecast(history, 64);
// point: median forecast
// quantiles[t] = [median, q10, q20, ..., q90] (same layout as timesfm.js)
// levels[t] = all 21 levels: q01, q05, q10, q15, ..., q95, q99
</script>
Hugging Face serves .js as text/plain, so browsers won't import it straight from this repo. Copy the file next to your page, or load it from the GitHub repo through jsDelivr. The model weights load fine straight from Hugging Face.
ONNX graph
- Inputs
patchesfloat32[batch, n, 48]: each 16-step context patch as (time encoding, scaled values, observed mask).patch_maskfloat32[batch, n]: 1 if the patch has at least one observed value.futurefloat32[batch, m, 48]: one row per output patch (time encoding, zeros, zeros).
- Output:
quantilesfloat32[batch, m, 21 × 16]: 21 quantile levels × 16 steps per output patch, in scaled space.
Both n (context patches) and m (output patches) are dynamic. Context can be up to 8,192 points and one pass forecasts up to 64 patches (1,024 steps).
Everything outside the network runs in chronos2.js, mirroring Chronos2Model._prepare_patched_context, _prepare_patched_future and forward from the official package: mean/std scaling, arcsinh, front-padding to a multiple of 16, time encoding (-C … -1 and 0 … h-1, divided by 8,192), then sinh and un-scaling on the way out. Each series is forecast on its own (no cross-series group attention). Horizons over 1,024 steps feed the median back in as new context. That is simpler than the official multi-path roll-out, so very long horizons can differ from the official package.
Verification
All numbers compare against Chronos2Pipeline.predict from chronos-forecasting 2.3.2 (PyTorch, CPU, float32) on 24 contexts: 14 synthetic series of 16–512 points (linear, seasonal, trend + season, random walk) and 10 real series held back from webTimesFM (COVID cases, world poverty, wheat, maize, US unemployment, CO₂, sunspots, Brent, WTI, Bitcoin). Differences are in units of each context's standard deviation.
| Build | Max difference from PyTorch, all 21 quantiles | Max difference, q10–q90 | Median drift vs fp32 on 16–64-point series (avg / worst) | Median drift on real series (worst) |
|---|---|---|---|---|
| fp32 ONNX | 8.1e-5 | 1.7e-5 | — | — |
8-bit (chronos2_q8.onnx) |
0.50 | 0.076 | 0.006 / 0.022 | 0.036 |
4-bit (chronos2_q4.onnx) |
8.4 | 0.42 | 0.11 / 0.36 | 0.25 |
Median drift is the mean gap between the quantised and full-precision median forecasts, measured in units of the series' average step size (the same measure as the TimesFM conversion). The large "all quantiles" figures come from the 1% and 99% tails of 16-point series, where the band is very wide. The median line and the q10–q90 band stay close.
Why the projections stay at full precision. Quantising every layer was tested first. 8-bit everywhere drifted 5× more (0.033 average, 0.095 worst). 4-bit everywhere drifted badly on short series (1.8 steps at worst, 3.1 on CO₂) and inverted its q10/q90 band on 39 forecast steps. Keeping the input and output projections (input_patch_embedding, output_patch_embedding, 11 MB) at full precision fixed most of that.
In the browser. chronos2.js with the fp32 file matched PyTorch to within 8e-5 (median 6e-6) on WASM and 9e-5 on WebGPU in Chromium, so the JavaScript pre- and post-processing reproduce the official code. The 8-bit file gave the same results on WebGPU and WASM as in Python, to 3 significant figures. On a laptop iGPU (WebGPU) one forecast took 55–85 ms; single-threaded WASM took 0.6–1.5 s.
Forecast accuracy on 10 real series. On the held-back points of the 10 real series, 8-bit and fp32 scored within 1% of each other (MAE) on 9 series, and within 4% on world poverty (a 5-point forecast). Side-by-side numbers against TimesFM 2.5 and a "no change" guess are in the webTimesFM comparison article.
These are small checks, not a benchmark. For accuracy on standard benchmarks, see the Chronos-2 paper and the fev-bench and GIFT-Eval results for the original model.
Changes from the original
Apache-2.0 §4(b) asks for a notice of modifications. This conversion:
- Exported the network (patch embedding,
[REG]token, 12 encoder blocks, quantile head) to ONNX opset 17 fromChronos2Pipeline.from_pretrained("amazon/chronos-2"), with dynamic batch, context and horizon axes. Eager attention was used instead of fused SDPA (same math). - Moved scaling, patching, time encoding and un-scaling out of the graph and reimplemented them in JavaScript.
- Quantised linear-layer weights to 8-bit and 4-bit with
onnxruntime.quantization.MatMulNBitsQuantizer(block 32, symmetric), keeping the input and output projections at full precision. - Dropped covariates and cross-series group attention from the browser wrapper (one series at a time), and used a median roll-out for horizons over 1,024.
The model weights' values come only from amazon/chronos-2. They were not fine-tuned or retrained.
Reproduce
pip install "chronos-forecasting>=2.0" onnx onnxruntime
python scripts/export.py out # writes out/chronos2_fp32.onnx and checks it against PyTorch
python scripts/quant.py out # q8, q8keep, q4, q4keep
python scripts/compare.py out <path to webTimesFM> fp32,q8,q8keep,q4,q4keep
python scripts/make_selftest.py # writes out/chronos2_selftest.json
chronos2_q8.onnx is q8keep and chronos2_q4.onnx is q4keep from quant.py.
Limitations
- Univariate, one series at a time. Chronos-2's covariate and multivariate features are in the ONNX graph's batch axis, but the wrapper doesn't expose them.
- First load downloads 153 MB (8-bit) or 96 MB (4-bit). Browsers cache it after that.
- The wrapper keeps quantiles ordered (q01 ≤ … ≤ q99) by default. Pass
{ fixQuantileCrossing: false }to get the raw output. - Not affiliated with Amazon.
License and citation
Apache-2.0, the same as the original Chronos-2 weights and code. The full text is in LICENSE.
@article{ansari2025chronos2,
title = {Chronos-2: From Univariate to Universal Forecasting},
author = {Abdul Fatir Ansari and Oleksandr Shchur and Jaris Küken and Andreas Auer and Boran Han and Pedro Mercado and Syama Sundar Rangapuram and Huibin Shen and Lorenzo Stella and Xiyuan Zhang and Mononito Goswami and Shubham Kapoor and Danielle C. Maddix and Pablo Guerron and Tony Hu and Junming Yin and Nick Erickson and Prateek Mutalik Desai and Hao Wang and Huzefa Rangwala and George Karypis and Yuyang Wang and Michael Bohlke-Schneider},
year = {2025},
url = {https://arxiv.org/abs/2510.15821}
}
Model tree for VishalMysore/chronos-2-onnx-web
Base model
amazon/chronos-2