Skip to content

feat: bindings for WebAssembly - #2450

Open
SBrandeis wants to merge 26 commits into
mainfrom
feat/wasm-bindings
Open

SBrandeis wants to merge 26 commits into
mainfrom
feat/wasm-bindings

Conversation

@SBrandeis

@SBrandeis SBrandeis commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

TL;DR

A new bindings library using https://github.com/wasm-bindgen/wasm-bindgen to generate a npm package that can be used in a browser context

Goal would be to publish it as a npm package so users can simply npm install tokenizers-web (or any other name) and use tokenize locally in their browser apps.

import { Tokenizer } from "tokenizers-web";

using tokenizer = Tokenizer.from_pretrained("openai/gpt-oss-120b");

const ids = tokenizer.encode("Hello world!");
console.log(ids); // Uint32Array [15496, 995, 0]
console.log(tokenizer.decode_tokens(ids)); // ["Hello", "Ġworld", "!"]
console.log(tokenizer.decode(ids)); // "Hello world!"

TODO

Release package

@HuggingFaceDocBuilderDev

Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

@ArthurZucker ArthurZucker left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!
Examples are cool, but imo a readme + pointing to the example space makes our changes minimal / better!

Let's try to update ptr_hash instead the maintainer is great and always responsive ! if possible !

Comment thread bindings/wasm/src/lib.rs
/// @param json - The contents of a `tokenizer.json` file.
/// @returns The tokenizer.
/// @throws If `json` is not valid JSON or does not describe a tokenizer.
pub fn from_json(json: &str) -> Result<Self, JsError> {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have from_file and from_pretrained in other APIs

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still to adress

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Browsers don't expose the user's file system so from_file is a bit moot here
To load from the user's file system you would use <input type="file"> and a handle like that:

input.onchange = async () => {
  const tokenizer = Tokenizer.from_json(await input.files[0].text());
};

from_pretrained has been implemented, however

@daaain

daaain commented Sep 26, 2026 •

Copy link
Copy Markdown

I've been compiling the 1.0 crates to wasm for my browser tokenizer playground and hit the same things, so a few notes from that:

  1. SIMD is compiled out. bitcannon's wasm kernel is behind cfg(target_feature = "simd128"), and .cargo/config.toml doesn't set it, so this build uses the scalar pre-tokenizer. Adding '-C', 'target-feature=+simd128' to the rustflags turns it on. But now that I measured it, in Node the flag makes no visible encode difference across six models, so maybe moot point.
  2. decode doc vs default: the doc for skip_special_tokens says false when not specified (src/lib.rs:63), but the code does unwrap_or(true) (:71). The test comment says skipping is intended, so I think the doc is what's wrong.
  3. node-release.yml: the diff moves contents: read back to the workflow level, which looks like it undoes Scope GITHUB_TOKEN permissions per job #2448. Maybe a rebase leftover?
  4. Per-token decoded text. decode_tokens gives vocab spellings (Ġworld), but a UI that labels each token needs decoded text ( world). Would a decode_each(ids) fit here? Mine is ~5 lines: daaain@b873c34. Returning one string plus offsets would be faster; I can measure that as a follow-up.
  5. Gemma 2 doesn't load (a core bug, not in this PR): the fix is in a separate PR, Load byte-fallback BPE vocabs that lack some <0xNN> codes #2461.
  6. cargo fmt --check fails on src/lib.rs and src/options.rs (trailing whitespace, one long line).

On ptr_hash: I'm happy to open the upstream PR Arthur suggested. Since the timings only feed trace!, taking timestamps only when log_enabled!(Trace) would avoid adding a web-time dependency.

Update: PR created RagnarGrootKoerkamp/PtrHash#41

Update 2: merged and released in PtrHash 2.1.2

daaain pushed a commit to daaain/online-llm-tokenizer that referenced this pull request Sep 26, 2026
…embly

Each model now loads and tokenises in its own Web Worker, sharing one
compiled WebAssembly module, so typing no longer blocks on tokenising.
Results for text that has changed since are dropped. Tokenizer files
are fetched from the Hub in the worker and kept with the Cache API
(the same cache transformers.js used).

The vendored build comes from daaain/tokenizers@claude/wasm-integration
(huggingface/tokenizers#2450 plus fixes in review upstream), and
scripts/tokenizers-wasm/ rebuilds it and checks parity with
transformers.js: all 14 tokenizers there match.

Also:
- A per-model "Clean up spaces before punctuation" switch, remembered
  in localStorage, showing the model's own default from
  tokenizer_config.json. It changes the text shown, never ids or count.
- Error cards say whether a repo is missing or gated, or has no
  tokenizer.json.
- The text box grows with field-sizing where supported, avoiding a full
  layout on every keystroke.
- Removes transformers.js and the experiment it replaces.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RuhhXa22BzYGGVpkkVnoco
@SBrandeis

Copy link
Copy Markdown
Collaborator Author

Thanks for porting the fix upstream in RagnarGrootKoerkamp/PtrHash#41 @daaain

@SBrandeis

Copy link
Copy Markdown
Collaborator Author

Returning one string plus offsets would be faster

Shipping offset tracking is in the pipeline for v1, fyi

@daaain

daaain commented Sep 28, 2026 •

Copy link
Copy Markdown

Thanks for porting the fix upstream in RagnarGrootKoerkamp/PtrHash#41

No worries, it was a simple fix!

BTW I got this branch's WASM version fully working in my online tokenizer and it's fast!

Here's a readme about the bits I needed to do (and the script I used), with the PtrHash fix in and SIMD flag added, it's pretty much there (just the Gemma 2 fix and the decode_each method missing): https://github.com/daaain/online-llm-tokenizer/tree/main/scripts/tokenizers-wasm

There's a transformers.js parity checker in there too if you're interested, but that might not be this branch's remit

@SBrandeis SBrandeis changed the title Feat/wasm bindings feat: bindings for WebAssembly Sep 28, 2026
@SBrandeis
SBrandeis marked this pull request as ready for review September 30, 2026 13:01

@ArthurZucker ArthurZucker left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM otherwise, trusting you entirely on the js / web but biding is alright! Le't sminimize files, code, etc if possilbe to have an as thin layer as possible!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO if we can point to a space, I'd rather have that 👀 or an externa tokenizers example repo?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy to volunteer my vanilla JS repo to be an example and to update to the canonical usage once this branch is merged and released!

Comment thread bindings/wasm/src/lib.rs
/// @param json - The contents of a `tokenizer.json` file.
/// @returns The tokenizer.
/// @throws If `json` is not valid JSON or does not describe a tokenizer.
pub fn from_json(json: &str) -> Result<Self, JsError> {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still to adress

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants