October 29, 2026

Spark-X2.6-4B

Running in your browser

XHToken's 4B model, using its official 4-bit weights, running on the GPU in Chrome and Edge. In testing it loses no measurable accuracy against the official INT4 release and generates about 90 tokens per second on a desktop GPU.

The chat app answering a coding question with thinking on
The chat app answering a coding question with thinking on. Everything runs on the local GPU.

Spark-X2.6-4B is a 4-billion-parameter model from XHToken, aimed at on-device agents: tool use, coding and long context, with a thinking budget that can be set to high, low or off. This build packages the official INT4 checkpoint as ONNX for onnxruntime-web. The model runs inside a browser tab, and what you type stays on your machine.

How Spark-X2.6-4B compares

XHToken reports these results for Spark-X2.6-4B against open models of similar size, all with thinking on. Across 30 benchmarks it averages 63.3, ahead of the larger Qwen3.5-9B (49.1) and Gemma4-12B (47.0). The lead is widest on agent work: web browsing, MCP tool use and software engineering.

How Spark-X2.6-4B compares

Reported by XHToken for the original Spark-X2.6-4B, thinking on at high effort. Source: the Spark-X2.6-4B model card on Hugging Face.

All 30 benchmarks

Spark-X2.6-4B benchmark results

* Taken by XHToken from that model's published card or paper. A dash means not reported. Gaokao 2026 is scored out of 150.

No measurable accuracy loss

A different runtime and 4-bit weights can change a model's answers, so the browser build was tested against the original model in transformers: same prompts, same chat template, greedy decoding, thinking off. Every browser number comes from the actual web page, run in headless Chrome.

The browser build against the original model

Spark-X2.6-4B, q4f16 build in Chrome. GSM8K: all 1,319 test problems. BFCL v3: the first 200 simple function-calling cases. Needle in a haystack: 12 prompts from 1k to 16k tokens.

The browser build is within measurement noise of the official INT4 checkpoint on all three. On GSM8K it solves 1,194 problems to the checkpoint's 1,203 and the original model's 1,198 (22 and 13 disagreements with the checkpoint, McNemar p = 0.18); function calling and needle in a haystack score the same. At the level of individual tokens, the build adds a mean KL divergence of 0.00055 on top of the INT4 quantization, and 30 minutes of back-to-back generation produced no errors and no slowdown.

Validation against the original model

A check passes when the browser build is not measurably worse than the official INT4 weights in transformers.

Speed

On a desktop GPU the model generates about 90 tokens per second and starts answering a short prompt in under 100 ms. Generation slows gently as the conversation grows, to 69 tokens per second at 16,000 tokens of context. Long prompts take longer to read before the first token: 1.7 seconds for 2,500 tokens.

From 21 to 88 tokens per second

Decode speed at 2,500 tokens of context after each change. The two largest steps came from keeping the KV cache in one GPU buffer that is updated in place, and from running the 4-bit matrix multiplies on float inputs.

Decode speed

tokens per second

Time to first token

seconds

Measured in Chrome on Windows with an NVIDIA RTX 3090 Ti, one request at a time. Speed depends on the GPU.

Get started

The model is a folder of static files and the loader is a few JavaScript modules, with no build step and no server code.

  1. Chat in the browser

    Open the chat in desktop Chrome or Edge. The model downloads once (2.77 GB) and loads from the browser cache afterwards.

  2. Run it locally

    Serve the app from a clone. It loads the model from Hugging Face unless a local build exists.

    git clone https://github.com/mohdmahmodi/Spark-X2.6-4B-onnx-webgpu
    cd Spark-X2.6-4B-onnx-webgpu
    npm install
    npm run serve
  3. Use it in your own app

    Copy web/lib/ and the import map from web/index.html. generate streams text and accepts temperature, maxNewTokens, effort, tools and an abort signal.

    import { loadSpark } from "./lib/spark.js";
    
    const spark = await loadSpark({
      base: "https://huggingface.co/mohdmahmodi/Spark-X2.6-4B-ONNX-WebGPU/resolve/main",
    });
    
    const messages = [{ role: "user", content: "Hello!" }];
    for await (const text of spark.generate(messages, { thinking: false })) {
      output.textContent += text;
    }
  4. Or use Transformers.js

    The same files load in Transformers.js 4.3 with dtype: "q4f16", with identical output. The loader above is faster at long context, since it keeps the KV cache in one preallocated buffer.

    import { pipeline } from "@huggingface/transformers";
    
    const generator = await pipeline("text-generation", "mohdmahmodi/Spark-X2.6-4B-ONNX-WebGPU", { device: "webgpu", dtype: "q4f16" });
    const out = await generator([{ role: "user", content: "Hello!" }], {
      max_new_tokens: 256, tokenizer_encode_kwargs: { enable_thinking: false },
    });
    console.log(out[0].generated_text.at(-1).content);

How it is built

The export writes the ONNX graph operator by operator from the checkpoint, using the fused kernels onnxruntime-web ships for WebGPU: grouped-query attention with a 512-token sliding window on 27 of the 36 layers, fused RMSNorm, and 4-bit matrix multiplies. The weights are XHToken's own INT4 codes, repacked without change, so the browser build runs the same quantized model the official checkpoint does. Round-to-nearest 4-bit was tried first and was far less accurate on this model.

The KV cache stays on the GPU in one buffer per layer, and a follow-up message reuses the cached conversation instead of reading it again. The source includes the export, the loader, the chat app and the full validation suite.

Limitations