Model converter

Simplify, optimize, and inspect ONNX models with onnxsim and onnx-optimizer, compiled to WebAssembly. Everything runs locally in your browser — models are never uploaded.

Also experimental: a Pyodide (Python-in-the-browser) demo running full onnxsim.simplify(), or onnxsim's native quantization passes on their own; see docs/wasm_pyodide.md for the toolchain story behind it.

versionsloading…

Parse a text graph

Paste an ONNX model or graph in the textual representation — the form onnx.parser.parse_graph / parse_model accept. It is parsed into a model by the WebAssembly module (a bare graph is wrapped into a model with a default-domain opset import) and shown in the Before Netron pane. Local function definitions (<domain: "..."> name (...) => (...) {...} blocks after the graph) are parsed too, and the wrapped model gains an opset import for each function's domain so it validates. With convert after parsing on, it is also run straight through the currently selected convert mode below (Simplify / Optimize / Inline functions, or a single debug pass).

Convert a model

⏳ Loading WebAssembly runtime… the file picker is disabled until it is ready.
Also accepts a standalone .onnx.safetensors or .onnx.gguf archive (e.g. one downloaded from the format selector below) -- it is decoded back to ONNX before converting.
Fetches the model straight from the Hugging Face Hub in your browser (nothing is uploaded), then runs it through the same Simplify / Optimize path as an uploaded file. Bare names default to the onnxmodelzoo org; any owner/repo or direct .onnx URL works too. Picking a model (or entering a repo id / URL) shows its download size first, so you can see how big it is before fetching. When a repo holds more than one .onnx, the largest is auto-detected; use the file selector to convert a different one.
Optional — only needed to load a gated or private repo you have access to; public repos (like onnxmodelzoo) don't need one. Create one at huggingface.co/settings/tokens ("read" access is enough). It is sent straight from your browser to huggingface.co as an Authorization header for size checks, file listing, and the download itself — never anywhere else, and never included in the "Copy shareable link" URL above. With remember off (the default) it lives only in this page's memory and is gone on reload; checking it saves the token in this browser's local storage (still local to your machine) so it survives a reload.
Shareable link: the input model and options can be set from the URL — e.g. ?model=onnxmodelzoo/resnet18d_Opset18 or ?model=https://…/foo.onnx&optimizer=optimize&cf=0. It loads and converts automatically (add &autoload=0 to only prefill). Keys: model (hf/url), graph (an ONNX text graph to parse), optimizer (a.k.a. mode/processor; the convert mode — simplify/optimize/optimize_fixed/inline/infer_shapes/data_propagation/fold_constant), constant_fold/cf, shape_inference/si, inline_functions/inline, tensor_size_threshold/tst, target_opset/opset, autorun/autoinfer (auto-run inference after each conversion), backend (prefills the backend-test URL). Setting an input updates this URL so it stays shareable (uploads excepted).
Builds a link that reproduces the current input and all the conversion options below (processor, constant fold, shape inference, tensor size threshold, target opset) and copies it to your clipboard. Uploaded local files have no URL, so share a Hugging Face model / URL, a backend test case, or a pasted text graph.
Downloads via @huggingface/hub, reconstructing the file from content-addressed chunks and caching them in IndexedDB so repeated / overlapping loads reuse them. On by default; falls back to a direct download automatically if the Xet path isn't available (e.g. a non-Xet repo or a CORS block).
The in-browser Xet client (XetBlob) fetches multiple content-addressed blocks at once and adapts the connection count to measured throughput, up to max connections, to saturate your link — this is what makes Xet fast enough to be the default (~10 or fewer is usually best; 1 forces a single serial connection). Xet also gives chunk dedup / cache reuse across repeated or overlapping loads.
Fetches model.onnx and the test data (test_data_set_N/ of input_*.pb / output_*.pb) from GitHub, runs the model through onnxruntime-web with the test inputs, and checks each output against the expected tensor within tolerance. Pick a preset above or paste a GitHub tree / raw.githubusercontent.com URL; listing uses GitHub's public (rate-limited) API. With convert the model on, the fetched model is also run through the selected convert mode below (and shown in the Netron panes).

Convert mode & options


single pass (debug):








Quantize a model

Quantizes a model with onnxsim's WebAssembly module, entirely in your browser -- nothing is uploaded. Its result is kept independent of the plain Convert section above (it never overwrites the "after" Netron pane or converted inference source) so you can inspect all three stages side by side: original -- onnxsim-simplified (Convert section, optional) -- quantized (its own "Quantized" Netron pane and quantized inference source below). Pick what to quantize:

Before picking a method, you can check whether INT8 (Dynamic / Static / QOperator's scheme) looks numerically safe for this model -- a static analysis of its own weights and shapes, no execution or calibration data needed, so it runs instantly.

Pick a method:

Calibration (Static/QOperator only) runs the model over a few batches of synthetic random input through onnxruntime-web (downloaded on demand, same as the "Run inference" panel below) to observe each quantizable tensor's actual value range -- the same approach onnxsim.calibrate() uses in Python, just run here in the browser instead. Random data is a reasonable stand-in when representative data isn't at hand, but real calibration data (e.g. via the Python API) usually gives tighter, more accurate ranges.



Quantized preview

Visualize with Netron (before / after)

Renders the model graph with a self-hosted Netron: the original model on the left and the simplified/optimized result on the right, so you can compare them side by side. Nothing is uploaded — the model bytes are posted straight into the embedded Netron in your browser, so there is no model-size limit. Use export SVG to save the rendered graph as an SVG image.
The quantized result has its own Netron preview inside the Quantize panel above instead of a third pane here, so it never crowds this before/after comparison.

Netron is driven here purely as an embeddable model-preview component over a postMessage protocol; that idea and protocol are being discussed upstream in lutzroeder/netron#1591.

Before
After

Dynamic dimensions (dim_param)

Lists the model's symbolic axis names (ONNX dim_params) on its graph inputs and outputs — before and after simplify/optimize — so you can see how the conversion changed the dynamic axes: a batch / sequence axis kept symbolic, or a symbolic dimension folded to a fixed size. Read straight from the model bytes in your browser (nothing is uploaded); symbolic axes are shown highlighted.

Try it: load Xenova/all-MiniLM-L6-v2 (a ~21 MB quantized MiniLM sentence-transformer) — its inputs and output carry the named dynamic axes batch_size and sequence_length, so they show up here on both the before and after sides. Most image models (e.g. the onnxmodelzoo ResNets in the dropdown) are fully static and list no dim_params.

Console outputs:

Download log saves the console output above as a .log file. Report an issue opens a pre-filled GitHub issue with the selected options, browser info, and the tail of the console output. Please also attach your model (and the downloaded log) so the problem can be reproduced.

onnxsim profiling

Profiling is on by default (“profile simplification” above): convert a model with Simplify and the fixed-point transforms (shape inference, onnx-optimizer, constant folding) — with ONNX Runtime's own per-operator constant-folding spans merged in — render below as a flame graph. Download the JSON, or click Embed in Perfetto to open the full Perfetto timeline inline on this page.

Node reduction per loop

How many nodes the graph held right after every round of each simplification fixed-point loop, until it stopped changing (a fixed point) or hit the round cap. One chart per loop — Optimize (shape inference + onnx-optimizer passes) usually does the most rounds; FoldConstant is the outer pipeline wrapping it.

Run inference (onnxruntime-web)

Runs a model for a few iterations to check it executes, using dummy inputs generated from the model's input shapes. The model picker chooses what to run: compare (before vs after conversion) — the default — runs both the original upload and the converted result on the same deterministic input and reports how far their outputs diverge (max |Δ|) plus the speed difference, so you can confirm the simplify/optimize step preserved the model's numerics and see how much faster it got. You can also run just the converted result or the original upload on their own. The batch control sizes the model's dynamic batch (first) axis; it has no effect on inputs whose first dimension is fixed. The warmup control runs that many untimed passes before the timed iterations, so one-time costs (kernel/program compilation, buffer allocation) don't add noise to the reported latency; set it to 0 to time every run. WebGPU falls back to WebAssembly when unavailable. Tick auto-run after each conversion to run this panel automatically whenever a conversion finishes (with the options selected here), instead of clicking Run inference each time. The input fill picker's sample data option feeds real data instead of synthetic numbers: a photo from frgfm/imagenette for an image-shaped input, or a tokenized sentence from stanfordnlp/sst2 for a recognized NLP model's input_ids/ attention_mask/token_type_ids — both fetched live from Hugging Face for each run. Any input that doesn't match one of those shapes, or an NLP model whose tokenizer family isn't recognized, falls back to random fill (logged in the output below). With sample data selected, a run also shows what it actually fed the model and got back — the fetched image/sentence next to the model's raw output, decoded as top-5 ImageNet-1k predictions when the output looks like a standard 1000-way classifier (a best-effort guess, not verified per model).

The execution-provider picker also offers WebNN (GPU / NPU / CPU), which runs the model through the browser's own ML stack. WebNN is experimental and ships behind a flag (Chrome/Edge: chrome://flags/#web-machine-learning-neural-network), with the broadest support on Windows — the WebNN status line below the controls reports whether it is usable in your browser, and any WebNN run falls back to WebAssembly when the chosen device or an operator is unsupported.

If the model carries onnxsim's MAC/FLOP metrics in its metadata_props (onnxsim PR #527, e.g. annotated via onnxsim.model_info.annotate_metadata), they are shown below with the achieved throughput (GFLOP/s = model FLOPs ÷ average latency). With annotate model info on (the default), the converter bakes these metrics into both the converted result and the original upload, so you can run each and compare their MACs alongside the measured inference speed.

WebNN status: probing…

With profiling on (default), the run captures an onnxruntime-web trace: on WebGPU, one span per GPU kernel invocation; on WASM (or as a fallback), one span per session.run iteration. It renders below in the same viewer, with an Embed in Perfetto button to open the full timeline inline.