GitHub - jehna/humanify: Deobfuscate Javascript code using ChatGPT

5 min read Original article ↗

Un-minify JavaScript code using LLMs ("AI")

This tool uses large language models (like ChatGPT, Claude, Gemini, and locally-hosted Ollama models) to unminify and rename minified JavaScript code. The LLM only suggests new identifier names; the heavy lifting is done by oxc at the AST level so the rewritten code remains structurally identical to the input.

Version 3 is out! 🎉

v3 highlights compared to v2:

  • Single static binary: (Rust) — grab a binary from Releases. No Node, no npm, no Python.
  • Unix-style I/O: read from stdin or a file, write to stdout or -o <file>. Combine easily with a deobfuscator like webcrack.
  • New providers: Ollama (local), Anthropic and OpenRouter.

Check out the v3 PR for more info.

➡️ Check out the introduction blog post for in-depth explanation!

Example

Given the following minified code in splitstring.min.js:

function a(e,t){var n=[];var r=e.length;var i=0;for(;i<r;i+=t){if(i+t<r){n.push(e.substring(i,i+t))}else{n.push(e.substring(i,r))}}return n}

Run:

humanify openai splitstring.min.js -o splitstring.js

Result (splitstring.js):

function splitString(inputString, chunkSize) {
  var chunks = [];
  var stringLength = inputString.length;
  var startIndex = 0;
  for (; startIndex < stringLength; startIndex += chunkSize) {
    if (startIndex + chunkSize < stringLength) {
      chunks.push(inputString.substring(startIndex, startIndex + chunkSize));
    } else {
      chunks.push(inputString.substring(startIndex, stringLength));
    }
  }
  return chunks;
}

You can also pipe via stdin:

cat splitstring.min.js | humanify openai - > splitstring.js

To unbundle Webpack output first, pipe through npx webcrack:

npx webcrack < bundle.min.js | humanify openai - -o bundle.js

Note on token usage

🚨 NOTE: 🚨

humanify makes one LLM call per identifier in your code. For ChatGPT-class APIs the cost roughly scales with the number of identifiers and the surrounding context window (default 500 chars per call). A medium minified file (~500 identifiers) typically costs in the range of $0.10–$1.00 with OpenAI's small models, free with the Gemini free tier, and free with Ollama or OpenRouter free models.

For a rough character-count estimate of OpenAI mode:

echo "$((2 * $(wc -c < yourscript.min.js)))"

Using humanify ollama is free but slower; quality depends on your local model. Free OpenRouter models (e.g. qwen/qwen3-coder:free) can help with your budget, but expect them to be heaviy rate limited.

Getting started

Installation

The preferred way to install humanify is to download a pre-built binary from the latest release.

# macOS (Apple Silicon)
curl -L https://github.com/jehna/humanify/releases/latest/download/humanify-aarch64-apple-darwin.tar.gz | tar xz
sudo mv humanify /usr/local/bin/

# macOS (Intel)
curl -L https://github.com/jehna/humanify/releases/latest/download/humanify-x86_64-apple-darwin.tar.gz | tar xz
sudo mv humanify /usr/local/bin/

# Linux (x86_64)
curl -L https://github.com/jehna/humanify/releases/latest/download/humanify-x86_64-unknown-linux-gnu.tar.gz | tar xz
sudo mv humanify /usr/local/bin/

# Linux (aarch64)
curl -L https://github.com/jehna/humanify/releases/latest/download/humanify-aarch64-unknown-linux-gnu.tar.gz | tar xz
sudo mv humanify /usr/local/bin/

# Windows: download humanify-x86_64-pc-windows-msvc.zip from the releases page

Or build from source:

cargo install --git https://github.com/jehna/humanify

Usage

humanify <openai|gemini|anthropic|ollama|openrouter> [FLAGS] <INPUT>
  • <INPUT> is a file path or - for stdin.
  • -o <FILE> writes to a file (default: stdout).
  • -m <MODEL> overrides the preset's default model.
  • -k <KEY> overrides the env-var-based API key.
  • --base-url <URL> overrides the preset's base URL.
  • --context-size <N> sets surrounding-code chars per identifier (default 500).
  • --json-mode <MODE> pins a JSON-mode strategy. Options: ladder (default), openai-json-schema, anthropic-native, forced-tool-call, tool-call-and-prompt, prompt.
  • -v enables verbose stderr logging.

Run humanify --help for the full reference.

Note: humanify does one job — rename identifiers in one JavaScript file in, one out. To unbundle webpack output first, pipe through e.g. webcrack:

npx webcrack < bundle.min.js | humanify openai - -o bundle.js

OpenAI mode

You'll need an OpenAI API key. Sign up at https://openai.com/ and create a key in the dashboard.

humanify openai obfuscated.js -o readable.js -k your-token

Or via environment variable:

export OPENAI_API_KEY=your-token
humanify openai obfuscated.js -o readable.js

Default model: gpt-5-mini. Override with -m.

Gemini mode

You'll need a Google AI Studio key. Sign up at https://aistudio.google.com/. Gemini's free tier is generous and is enough for most files.

export GEMINI_API_KEY=your-token
humanify gemini obfuscated.js -o readable.js

Default model: gemini-3.1-flash-lite. Override with -m.

Anthropic mode

You'll need an Anthropic API key. Sign up at https://console.anthropic.com/.

export ANTHROPIC_API_KEY=your-token
humanify anthropic obfuscated.js -o readable.js

Default model: claude-sonnet-4-6. Override with -m.

The Anthropic preset uses Anthropic's native structured-outputs API (output_format: json_schema) when available, falling back to forced tool-calls if your account doesn't have the structured-outputs beta enabled.

Local mode (Ollama)

Local mode runs against Ollama, which manages local LLM weights and exposes an OpenAI-compatible API on localhost:11434. (pre-v3 migration note: There's no humanify download anymore — use a local inference provider like Ollama to run your own models)

Prerequisites:

  1. Install Ollama: https://ollama.com/download
  2. Pull the recommended model: ollama pull qwen3.5:4b

Then run:

humanify ollama obfuscated.js -o readable.js

Default model: qwen3.5:4b. Override with -m to use any model you've pulled. Local mode is free and private, but slower and less accurate than the hosted providers; quality depends on the model you pick.

If you want to point humanify at a remote Ollama instance, override the base URL:

humanify ollama obfuscated.js --base-url http://my-server:11434/v1

OpenRouter mode

OpenRouter routes requests across many backend models. Useful for trying free-tier coding models without setting up multiple accounts.

You'll need an OpenRouter API key. Sign up at https://openrouter.ai/.

export OPENROUTER_API_KEY=your-token
humanify openrouter obfuscated.js -o readable.js

Default model: openai/gpt-oss-120b. For free-tier usage:

humanify openrouter obfuscated.js -m qwen/qwen3-coder:free

Features

  • Uses LLMs to get smart suggestions to rename variable and function names, and make the rename using deterministic AST-level shenanigans via oxc
  • Renames preserve all references and respect lexical scoping
  • Reserved-word and collision-aware safe naming. The LLM's suggestion is normalised to a valid JS identifier and _-prefixed if it collides with an existing binding

Contributing

If you'd like to contribute, please fork the repository and use a feature branch. Pull requests are warmly welcome.

git clone https://github.com/jehna/humanify
cd humanify
cargo build
cargo test

CI runs cargo fmt --check, cargo clippy -D warnings, cargo test on every PR. Provider e2e suites run against gemini (every PR, free tier) and ollama (every PR, runs on the GitHub runner). Other providers' e2e suites are label-gated (test-openai, test-anthropic, test-openrouter) to avoid burning API credits on every PR.

Star History

Star History Chart

Licensing

The code in this project is licensed under MIT license.