Library / SDK
jenissimo/unfake.js avatar
jenissimo/unfake.js

unfake.js is marked private, ships a Rust core numbered 0.3.0, and points at another GitHub org

Fix AI pixel art and vector images right in your browser

927 stars60 forksJavaScriptMIT

At a glance

What is it?
unfake.js cleans up AI upscaled pixel art and traces raster images to SVG, with a WASM core written in Rust and a browser tool built on Tweakpane. Its packaging tells three different stories at once: nothing is published to npm, the Rust workspace carries its own version and its own repository URL, and one build script points at a directory the tree does not have.
Who is it for?
unfake.js is a good fit for a browser project that already builds with Bun and can serve its tool directory over http, and for pipelines that want pixel grid detection and palette control without writing their own. It is a poor fit for a Node or npm consumer expecting to add it as a dependency, because the package is marked private and every documented import is a relative path.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 3, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The package is marked private and every documented import is a path

There is no npm package to add. The manifest sets private to true, so publishing is refused, and it declares no entry point field at all. The usage examples import the library by relative path:

javascript
import unfake from './lib/index.js';

That is the whole integration story for JavaScript consumers, copy the directory or vendor it. The build and deploy tooling assumes Bun rather than Node or Python: the scripts are invoked as `bun scripts/vendor-copy.mjs` and `bun scripts/deploy.ts`, a Bun lockfile is committed, and there is no npm lockfile. Development dependencies are three entries and nothing else:

code
"devDependencies": {
    "@types/bun": "latest",
    "fflate": "^0.8.2",
    "typescript": "^5.8.3"
}

So the library reaches users through a directory copy, an itch.io demo page, a third party ComfyUI node, and a third party Python port that claims a ten to twenty percent speedup over the JavaScript version. The linked homepage in the project metadata is that itch.io demo rather than a documentation site, and the two long form write ups that exist are one in English on a developer blog platform and one in Russian on a news site, so neither is versioned with the code.

Three Rust crates sit behind the JavaScript, one of them undocumented

The heavy lifting moved into a Rust workspace with three members: unfake-core, unfake-cli, and unfake-wasm. Only two of those are described anywhere in the prose. The wasm crate is the one the browser sees, built through the scripts named core:build and core:wasm:

code
"core:build": "cargo build --release",
"core:wasm": "bun scripts/build-core-wasm.mjs",
"core:test": "cargo test"

That means a contributor needs a Rust toolchain in addition to Bun, and the test suite for the core is a Cargo test rather than a JavaScript test. The workspace pins the image crate at 0.25 and wasm-bindgen at 0.2, and pulls in clap 4 with the derive feature, which exists to give unfake-cli a command line. A crate with a command line parser, a binary target for evaluation, and no user-facing documentation is the part of this repository most likely to surprise someone who only read the readme. Two lockfiles are committed, one for each ecosystem, so a contributor has two dependency graphs to keep consistent rather than one.

The Rust workspace has its own version and its own repository

The two halves of the project are numbered independently, and the Rust half also names a different home. The workspace package block reads:

code
[workspace.package]
version = "0.3.0"
edition = "2021"
license = "MIT"
repository = "https://github.com/unfake-js/unfake.js"

The JavaScript manifest says version 1.4.0, and the current release tag is v1.4.0, so the JavaScript line is coherent on its own. But the Rust workspace is at 0.3.0 even though the v1.2.0 tag is titled as the release that brought in the Rust and WebAssembly core, meaning the component that now does the quantization and the filtering carries a pre one version number two releases later. The repository URL is the sharper problem: it points at an organization named unfake-js, while the code, the readme, and the issue links live under a different owner entirely. Anything that resolves the upstream automatically, a badge, a crate manifest, a dependency scanner, will follow that URL and may land somewhere that is not this project.

The evaluation script names a directory the tree does not contain

One script points outside the repository:

code
"core:eval": "cargo run --release -- eval-bghira experiments/data/bghira"

It expects a binary target called eval-bghira and a data directory at experiments/data/bghira. Neither appears among the top level entries, which hold the crates directory, the scripts directory, the browser tool, the two demo images, the Cargo and package manifests, and the lockfiles. So the evaluation path is either generated locally, downloaded separately, or left over from a private tree. That is the only script in the manifest with that problem. The other four, vendor, deploy, deploy:zip, core:build, core:wasm, and core:test, all reference files that are present. Anyone auditing whether the evaluation harness can be reproduced from a clean clone will not find its data.

imagetracer.js and libimagequant arrive as vendored files

The manifest has no runtime dependencies block at all, yet the vectorizer is described as a wrapper around imagetracer.js and the palette step runs on imagequant, the libimagequant binding, inside the WebAssembly core. Neither library is declared as a dependency. A vendor script exists to copy them in, so the tracing engine and the quantizer are files in the tree rather than packages from a registry. That is consistent with a browser tool that must load synchronously from a local directory, and it is also a supply chain question worth asking about directly: the version of the tracer you get is whatever was copied last, with no upper bound recorded anywhere in the manifest. The three declared dev dependencies, a Bun type package, fflate for compression, and TypeScript, cover tooling only. Vendoring also buys the offline property the project advertises: once the tracer is a file in the tree, the browser tool needs no registry at runtime, which is the point of serving it from a local server in the first place.

The browser tool refuses to open from the filesystem

Opening the tool by double clicking an index file does not work, and the reason given is specific: modern browsers do not allow ES module imports from file URLs, which includes import maps and module scripts. The tool lives in a browser-tool directory and must be served. Three recipes are given, and they assume three different things you may not have installed:

sh
python -m http.server 8080
# or
python3 -m http.server 8080
sh
npx http-server -p 8080

The third option is the Go Live button in an editor, pointed at the browser-tool folder. In every case you then open http://localhost:8080/browser-tool/. What you get there is a Tweakpane panel for every processing parameter, drag and drop or clipboard paste as input, a side by side view, a magnifier for checking pixel level detail, and a palette editor that lets you swap individual colors in the result before you download it.

Fractional and elastic grids are what the newest release added

The current release is titled for the grid handling, which addresses the awkward case of models that resample their canvas. The example given is 200 cells across 2048 pixels, which is 10.24 pixels per cell, so no integer scale factor exists and the detector has to work with a fraction. Detection itself picks between runs based and edge aware methods, or accepts a manual factor:

javascript
const options = {
    file: file,
    maxColors: 32,
    detectMethod: 'auto', // 'auto', 'runs', 'edge'
    downscaleMethod: 'dominant',
    snapGrid: true,
    cleanup: {
        morph: true,
        jaggy: true
    }
};

Where hand drawn cells jitter around the detected scale, an elastic grid fits cell boundaries to the drawn edges instead. Downscale methods include dominant, median, and content adaptive, palette reduction runs through imagequant with a custom palette option, and alpha binarization exists for hard transparency. The two cleanup flags in the example are morphological, which fills holes and removes noise, and jaggy, which smooths stair stepping. A manifest comes back alongside the image data and the palette, so a pipeline can record what was detected rather than guess later.

Vectorization wraps the tracer in passes on both sides

The second mode treats tracing as the middle step of a longer pipeline. Before tracing, a bilateral or median filter reduces noise, the palette is reduced and can be sized automatically, and transparent images get a temporary background that is stripped again afterwards to avoid edge artifacts. After quantization a gentle blur softens the jagged edges that reduction creates. The tracer options are passed through rather than hidden:

javascript
const options = {
    file: file,
    preProcess: {
        enabled: true,
        filter: 'bilateral',
        value: 15
    },
    quantize: {
        enabled: true,
        maxColors: 'auto' // or a number like 16
    },
    // imagetracer.js options
    ltres: 1,
    qtres: 1,
};

The output is an SVG string plus an extracted palette and a manifest. In the browser tool the same result arrives as a download of a .png or an .svg, or on the clipboard. The pixel and vector examples in the usage section both stop mid statement, after the object creation and the first line of result handling.

Editorial conclusion

unfake.js is a good fit for a browser project that already builds with Bun and can serve its tool directory over http, and for pipelines that want pixel grid detection and palette control without writing their own. It is a poor fit for a Node or npm consumer expecting to add it as a dependency, because the package is marked private and every documented import is a relative path. Before adopting it, check three things: that you can build the Rust core, since the quantizer and the filters live in WebAssembly rather than in JavaScript; that the version numbers you are reading mean what you think, because the JavaScript package and the Rust workspace are numbered independently; and that the repository you clone is the one your tooling expects, since the workspace metadata names a different organization than the one hosting the code.

Frequently asked questions

Can I install unfake.js from npm as a dependency?

No. The package manifest is marked private, which blocks publishing, and declares no entry point, so the documented usage imports a relative path such as ./lib/index.js instead of the package name. You would vendor the directory into your own project.

Why does the unfake.js browser tool need a local server?

Because modern browsers do not allow ES module imports from file URLs, so the tool cannot be opened directly from disk. Serve the project root over http and open the browser-tool directory, for example with python -m http.server 8080 and then http://localhost:8080/browser-tool/.

What does unfake.js do about pixel grids that are not whole numbers?

It detects the true cell size with runs based or edge aware methods, including fractional grids such as 200 cells across 2048 pixels, which works out to 10.24 pixels per cell. Where cells jitter around that scale an elastic grid fits the boundaries to the drawn edges.

Are the Rust core and the JavaScript library released under the same version number?

No. The JavaScript manifest and the current tag are both at 1.4.0, while the Rust workspace package version is 0.3.0, even though the release tagged v1.2.0 introduced the Rust and WebAssembly core. The two halves are numbered separately.

Official sources

  1. jenissimo/unfake.js on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/jenissimo-unfake-js.svg)](https://hysenlabs.com/projects/jenissimo-unfake-js)