# AfterPack vs other JavaScript obfuscators

How AfterPack compares to javascript-obfuscator, js-confuser, JScrambler, JSDefender and ByteHide, and why per-build polymorphism requires no fixed structure underneath it.

Source: https://www.afterpack.dev/docs/comparison

Every obfuscator claims to resist reverse engineering. What matters is [what remains readable](https://www.afterpack.dev/docs/reversal-classes) after a deobfuscator or a frontier model processes the output.

We benchmark the open-source obfuscators (javascript-obfuscator, js-confuser) on our own harness and publish the comparative results here and on the landing page — see [How we measured](#how-we-measured). The commercial tools (JScrambler, JSDefender) sit behind a sales-gated trial we have not yet obtained, so they are marked pending and described from vendor documentation only; ByteHide is a cloud service, not a local build step, so it has no comparable local-build figure and is likewise described from its vendor pages only. AfterPack's own standalone numbers are on [Performance](https://www.afterpack.dev/docs/performance).

## What survives a deobfuscation pass

In one public test, a coding agent stripped both `javascript-obfuscator` and a commercial tool (JScrambler) back to readable logic in about ten minutes ([full run on our blog](https://www.afterpack.dev/blog/ai-deobfuscates-javascript)). This happens to any obfuscator with a fixed output shape: a string array, even encrypted, a custom VM interpreter, or a self-redefining decoder bootstrap. `webcrack` and `restringer` hardcode recognizers for these shapes and strip them in microseconds; a frontier model does the same with a prompt instead of a parser.

Tools that vary output build to build do not escape this if the underlying structure stays fixed: they are polymorphic in the constants only, so a recognizer still matches every build of that version. AfterPack's output has [no fixed structure](https://www.afterpack.dev/docs/concepts#no-peelable-obfuscation-layers) for a recognizer to hardcode against, so a deobfuscator written against one build does not carry to the next. Reversing one build teaches an attacker little about [the next](https://www.afterpack.dev/docs/config#seed) — measured directly, across six seeds and five samples, under 6% of what a deobfuscator recovered on one build still resolved on the next ([how we measured](#how-we-measured)).

*(animated "no peelable layers" comparison — see the web page)*

For other tools, an attacker writes a recognizer once and reuses it against every release; against AfterPack, that investment never pays off. The [threat model](https://www.afterpack.dev/docs/threat-model) quantifies this attacker by attacker.

## Feature comparison

The same six vendors and eight rows compared on the landing page, in the same order — a row is here
only if AfterPack's cell differs from at least two competitors and a buyer would ask the question.

> **Where these rows come from**
>
> The first three rows are measured on our own harness — see [How we measured](#how-we-measured) below.
> AfterPack's column in the rest comes from our own documentation and shipped product. Every other
> column is what that vendor publishes, and where a vendor publishes nothing, the cell says so.

| Feature | AfterPack (0.1.0) | javascript-obfuscator (5.6.0) | js-confuser (2.1.3) | JScrambler | JSDefender | ByteHide |
|---|---|---|---|---|---|---|
| [Build time](#how-we-measured) | 381 ms | 4.6 s (12× longer) | 10.6 s (28× longer) | Not run | Not run | N/A — builds in the cloud |
| Output size, gzipped | 4.2× the minified bundle | 10.9× | 12.3× (single unseeded draw) | Not run | Not run | Not run |
| [Strongest preset that still runs](#how-we-measured) | `extreme` (top preset, 5/5) | `medium` (`high` hangs in headless CI, 0/5) | `high` (top preset, 5/5) | Not run | Not run | Not run |
| [Price & free tier](https://www.afterpack.dev/docs/tiers) | Free, unlimited local — paid from $49/mo | Free, open source — $19/mo VM add-on | Free, open source | Quote only | Quote only | Book a demo |
| Framework plugins | 13 first-party plugins | Community loaders | Node API only | CLI + API | None found | Plugins, vendor-claimed |
| Runs at the edge | Yes — WASM + Workers | Not documented | Not documented | Not documented | Not documented | Not documented |
| CSP-safe output (no `eval`, no `new Function`) | Yes, by default | Not documented | Not documented | Not documented | Not documented | Not documented |
| [Deobfuscator carry-over](#how-we-measured) | Under 6% carries over — new opcode table every build | Carries over — fixed shape (`webcrack`, `restringer`) | Public write-ups (not reproduced by us) | Vendor: polymorphic (not independently run) | Carries over — fixed pipeline, per its own docs | Not documented |

## How we measured

Machine: one Apple M-series Mac, Node 22. Every figure below is from September 2026 and reproducible
from the same inputs; nothing here is provisional.

**Build time and output size.** A single real, already-minified production bundle, run end to end,
wall-clock. AfterPack and js-confuser ran at their top preset (`extreme` and `high`).
javascript-obfuscator's top preset, `high-obfuscation`, enables an anti-debugger that hangs under
headless or CI execution, so it ran at `medium-obfuscation` instead — its strongest preset whose
output still completes.

**Strongest preset that still runs.** The same run, scored against five executable samples in headless
CI. AfterPack and js-confuser pass 5/5 at their top preset; javascript-obfuscator scores 0/5 at `high`
(the anti-debugger above) and 5/5 at `medium`, which is the number the table reports.

**Deobfuscator carry-over.** A separate measurement, not the build above. We build a deobfuscator
against one AfterPack build, then run it against a fresh build from a different seed, across six seeds
and five samples. Under 6% — 2.1% to 5.6% across the six pairs — of what the deobfuscator recovered on
the first build still resolved on the next. Any build can be reversed with enough effort, including
ours; this measures whether that effort carries to the next build, not whether it succeeds at all.

JScrambler, JSDefender and ByteHide are not yet in this harness — their trials are sales-gated and we
have not obtained one. Their price, plugin, edge and CSP rows come from public documentation; their
measured-band cells read "Not run" until we do.

## vs javascript-obfuscator

The free, open-source default most teams reach for first, and the tool `webcrack` was written against. Its output is a fixed pipeline: a string array, a rotation-decoder bootstrap, and control-flow flattening with a recognizable switch shape, once a barrier and now a signature.

- The core is Rust and ships as a native binary or WebAssembly, not a JavaScript library. On our own harness ([how we measured](#how-we-measured)), AfterPack protected the same real, minified production bundle in 381 ms against javascript-obfuscator's 4.6 s (12× longer), and shipped output 2.6× smaller (4.2× the minified bundle versus 10.9×).
- AfterPack has no fixed structure for `webcrack` to hardcode against.

Both are free; [AfterPack](https://www.afterpack.dev/docs/tiers) adds unlimited builds and no credit card. What survives a `webcrack` pass or a short LLM conversation is the real difference.

## vs js-confuser

A newer open-source alternative, also free (MIT). On our harness ([how we measured](#how-we-measured)), the same bundle came out 12.3× the minified bundle's size — about 13% larger than javascript-obfuscator's already-inflated 10.9× and roughly 2.9× larger than AfterPack's 4.2× — at a build time of 10.6 s (28× AfterPack's). All five executable samples still ran correctly at its top preset (`high`) in that run, the one row where js-confuser did not have to fall back the way javascript-obfuscator did.

- js-confuser documents no seed option, so output is not deterministic build to build — a fixed input can produce different bytes each run.
- Public write-ups document recovering js-confuser's original logic; we have not reproduced this ourselves.

## vs JScrambler

The commercial incumbent, reached behind a sales call and an enterprise contract. Its base tier ships pattern-recognizable structure.

JScrambler's documentation states that its protections are polymorphic by default, and that protecting the same input twice does not produce the same output. It documents a randomization seed for reproducing one protection exactly.

- A published, self-serve price ($49/mo Indie) instead of a sales-gated contract.
- The [Free tier](https://www.afterpack.dev/docs/tiers) beats most starter commercial tiers on raw capability, with no account needed.
- [Enterprise](https://www.afterpack.dev/docs/enterprise) covers teams that cannot send source to a third-party API. The zero-egress option is the [free local engine](https://www.afterpack.dev/docs/deployment-modes).

## vs JSDefender

PreEmptive's commercial JavaScript protector. The engine and the CLI are not on public npm: they are downloaded from an authenticated account portal, and the license is checked over the network on every run, so a build cannot run offline or after the license lapses. The license is a floating license with a limit on concurrent builds.

The pipeline is a fixed set of thirteen code transforms, each switched on or off. Two are on by default: string-literal extraction into a coded table, and renaming of declared variables and functions. The rest cover control-flow flattening, boolean and integer literal encoding, property indirection, expression sequencing, function reordering and variable grouping. Separate options add environment locks: a self-defending integrity check, devtools blocking, console cloaking, a domain lock and a date lock. The JSDefender runtime is injected into the protected artifact, which is what public deobfuscators use: three open-source tools recover the original logic by running the decoder that ships next to the protected code.

Output is not polymorphic. The pipeline is the same on every build, and a `randomSeed` option makes the output byte-stable for a given seed, so a recognizer written against one release still matches the next.

Output size, speed and behavior under the strongest configuration are not documented, and we publish no figures of our own for them.

A file AfterPack cannot protect [fails the build](https://www.afterpack.dev/docs/diagnostics) instead of shipping.

## vs ByteHide

A cloud-first protection service: builds run on ByteHide's infrastructure rather than as a local step, gated behind booking a demo, with no public pricing page. That also means there is no local build-speed figure to put beside a tool that runs on your machine — the comparison itself does not apply, not that ByteHide is slow.

ByteHide publishes a benchmark of its own claiming +0.4 ms of added startup time and markets anti-tamper protection; we have not independently verified either claim, and its CLI documents a flag for source maps. It does not publish whether builds are reproducible from a given input.

- [AfterPack's free tier](https://www.afterpack.dev/docs/tiers) runs entirely on your machine, unmetered, with no account, no card and no demo call.
- Sending source to any cloud build step — ByteHide included — is the tradeoff [Enterprise](https://www.afterpack.dev/docs/enterprise) and the [free local engine](https://www.afterpack.dev/docs/deployment-modes) exist to avoid.

## vs Terser

Terser is a different category of tool: it minifies, shortening names and folding constant expressions well. Minified code keeps all of the original logic, so it offers no resistance to an LLM reading the result. Run Terser, then AfterPack, or let AfterPack's own [`minify` preset](https://www.afterpack.dev/docs/presets#the-ladder) handle both.

## What a static attacker recovers

| Tool | What a static reader recovers |
|---|---|
| Minified only | Everything. A frontier LLM explains minified code about as easily as formatted code, since only the names changed. |
| javascript-obfuscator | Effectively everything. Fixed-pipeline output means `webcrack` reverses it in seconds. |
| js-confuser | Public write-ups document recovering its original logic; we have not reproduced this ourselves. |
| JScrambler (base tier) | Real but pattern-recognizable structure. |
| JSDefender | Effectively everything. The decoder ships in the artifact, so a public tool runs it and prints the original logic back. |
| ByteHide | Vendor-claimed anti-tamper; no independent, public verification found. |
| **AfterPack Free** | An equivalent program with the same behavior: [renamed & encoded plus flattened & fused](https://www.afterpack.dev/docs/reversal-classes) classes hit the whole bundle, and the next build is unrelated to this one. |
| **[AfterPack Pro](https://www.afterpack.dev/docs/pro)** ([marked regions](https://www.afterpack.dev/docs/directives#what-you-can-set)) | An equivalent program with the same behavior: the same two classes as Free, applied surgically to marked regions at a much higher complexity target. |

Full class definitions live on [Reversal classes](https://www.afterpack.dev/docs/reversal-classes) and [How AfterPack works](https://www.afterpack.dev/docs/concepts#what-an-attacker-can-actually-recover). Verify your own build with the [Protection Map](https://www.afterpack.dev/docs/protection-map).

## Scope of this comparison

[Given enough time, every obfuscator's output can be reversed](https://www.afterpack.dev/docs/threat-model#what-obfuscation-protects), including AfterPack's. Fixed-pipeline tools give an attacker a one-time investment that pays off across every build; AfterPack removes the fixed structure, so that investment does not carry from one build to the next.

## Next

- [How AfterPack works](https://www.afterpack.dev/docs/concepts): reversal classes and secret survival explained in full.
- [Tiers](https://www.afterpack.dev/docs/tiers): the Free/Pro boundary and where the surgical Pro transforms apply.
- [Threat model](https://www.afterpack.dev/docs/threat-model): the adversary ladder every claim above is scoped against.
- [Audit](https://www.afterpack.dev/docs/audit): scan any live site, yours or a competitor's, for what it currently leaks.
