Library / SDK
MichaelXF/js-confuser avatar
MichaelXF/js-confuser

JS-Confuser rewrites your JavaScript so it cannot be read

JS-Confuser is a JavaScript obfuscation tool to make your programs *impossible* to read.

525 stars68 forksTypeScriptMIT

At a glance

What is it?
JS-Confuser is a JavaScript obfuscator that parses your code into an AST and rewrites it: variables are renamed, control flow is restructured, strings are concealed, functions are wrapped, and optional locks and integrity checks are added. It runs as a Node library or in the browser playground, and its honest value is raising the cost of reading shipped code, not protecting secrets.
Who is it for?
JS-Confuser fits a team that ships browser JavaScript, wants to slow down casual reverse engineering of the shipped bundle, and can live with harder debugging and a build step in the pipeline. It does not fit a team hoping to keep secrets in the client, since anything in the bundle can be recovered.
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 70 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

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

Editorial analysis

What obfuscation is actually for in a browser bundle

JS-Confuser describes itself as a JavaScript obfuscation tool to make your programs impossible to read, and the site it links to is a web version of the tool you can try in a browser. The npm package is js-confuser, and the licence is MIT.

The use case is narrow and worth stating plainly before the features. When you ship JavaScript to a browser, the source that runs on the user's machine is the source the user can read. Obfuscation does not change what the program does, it changes how expensive it is to understand. A competitor who receives your bundle cannot lift a well known algorithm from it in an afternoon if the control flow has been flattened, the strings are concealed behind runtime lookups and the variable names are noise.

That is a speed bump, not a lock. Anyone determined enough can still run the code, watch its behaviour, and recover the logic. So the audience is a team that believes some level of effort is worth spending, typically one shipping a paid client side product, a closed source SDK, or a large front end whose business logic would be useful to somebody else. A team that wants to protect a secret should keep the secret on a server, and no option in this tool changes that.

The second audience is anyone who wants to publish code that others can read but not easily copy, which is why the project also ships an integrity feature that detects changes to source code, and why a browser playground exists at all.

The transform list, from renaming to locks

The README lists the key features, and each one is a separate transformation you can combine.

Variable renaming replaces your identifiers with generated ones, which is the cheapest transform and the one every minifier already does. Control flow obfuscation restructures the program so that the order of statements is hidden behind conditions and loops. String concealing moves literals out of the code and behind a runtime lookup, so that a string like an API endpoint or a feature flag is not visible in the bundle. Function obfuscation wraps functions so their bodies cannot be read in a straightforward way, often by moving the body behind a generated wrapper and a string array.

Then there are the two features that are not obfuscation in the usual sense. Locks, of which the README names domainLock and date, restrict where and when the code will run, so a bundle licensed for one domain will not execute on another, and a date lock can expire a build. And there is a feature to detect changes to source code, documented separately as integrity, which is a check that the code has not been modified since it was built.

The combination of these is what makes the tool more than a minifier, and the point of a preset system is that most people should not pick transformations one by one. The usage example picks a preset called high, which implies a level rather than a specific list, and the documentation links separate pages for options and for presets.

The pipeline is Babel, which is why it survives modern syntax

The implementation gives away how the tool works. The runtime dependencies are the Babel toolchain, specifically the parser, the traverse module, the generator and the types package.

json
  "dependencies": {
    "@babel/generator": "^7.28.0",
    "@babel/parser": "^7.28.0",
    "@babel/traverse": "^7.28.0",
    "@babel/types": "^7.28.2"
  },

So the pipeline is parse, transform, print. Your code is parsed into an abstract syntax tree, a set of visitors rewrites the nodes in that tree according to the options you passed, and the generator prints the tree back out as JavaScript. That is why the tool can handle modern syntax rather than only the subset a regular expression based obfuscator can match, and it is also why the output is regenerated code rather than your code with parts mangled.

The project itself is written in TypeScript and compiled with Babel, and the build scripts show the pipeline for the tool rather than for your code. The type declarations are bundled with dts-bundle-generator, the type check is a plain tsc run, and publishing runs the build and the tests first.

json
    "typecheck": "tsc --noEmit",
    "prepublishOnly": "npm run build && npm run test"

One consequence of the tree rewriting approach is worth keeping in mind. Because the output is regenerated from a modified tree, stack traces, function names and anything that depends on the shape of your code at runtime will differ from the original, and a debugger session against the shipped bundle will be a debugging session against different code.

Calling obfuscate() from a build script

The API is a single function that takes your source as a string and an options object, and returns a promise of the result.

js
var JsConfuser = require("js-confuser");

JsConfuser.obfuscate(`
  function fibonacci(num){   
    var a = 0, b = 1, c = num;
    while (num-- > 1) {
      c = a + b;
      a = b;
      b = c;
    }
    return c;
  }

  for ( var i = 1; i <= 25; i++ ) {

The options in the example are the target, which selects the runtime the output is meant for, the preset, which selects how aggressive the transformation is, and stringEncoding, which the comment in the example points out is normally enabled and is switched off there so the sample output stays readable.

js
  }
`, {
  target: "node",
  preset: "high",
  stringEncoding: false, // <- Normally enabled
}).then(result => {
  console.log(result.code)
})

So the shape of a build step is: read the file, hand the text to obfuscate with your options, write result.code back out. The installation is the usual one for a Node package.

bash
$ npm install js-confuser

The README also points at a web playground, a page explaining what obfuscation is, an options reference and a FAQ, and all of the documentation lives on the project's own site rather than in the repository, with a docs/ directory in the tree as well. That arrangement is worth noting for anyone planning to depend on it: the behaviour of every option lives in documentation you read on a website, and the repository holds the implementation and its tests.

Presets decide how much you are signing up for

The single most consequential choice you make is the preset, and the documentation treats presets as a first class topic, separate from the options reference.

That separation is the useful bit. Options are the individual transformations, and presets are curated combinations of them. The example in the README uses the preset named high, which by the naming convention implies a ladder from lighter to heavier settings, and the presets page is where you find out what each level actually turns on, since a high preset with control flow flattening and string encoding produces output that is far harder to debug than a low one that mostly renames.

This is the part of the tool that has to be chosen with your workflow in mind rather than your security posture in mind. The heavier the transformation, the more your own stack traces, error messages and profiling become noise, and the more time you spend mapping a production bug back to source through a source map that may itself be missing. A team that ships a large client side bundle and needs to debug it in production has a genuine reason to prefer a light preset and to keep source maps private, and a team shipping a small self contained widget has no such constraint.

The site is also where the playground lives, and it is the fastest way to see what a given preset does to your own code before you wire it into a build. Pasting proprietary code into a third party web tool is a decision with its own risk profile, and the npm package has no such exposure.

Where obfuscation is the wrong tool

Three failure modes are worth naming, and none of them is a bug.

The first is the client side secret. A domain lock, a date lock and an integrity check are all enforced by code that ships to the machine of the person you are trying to stop. An attacker who wants to remove them is editing the bundle they already have, and the obfuscator makes that harder, not impossible. If a licence key, a signing secret or a business rule must be enforced, that enforcement has to happen on a server, and the right answer is an API call rather than a lock in the bundle.

The second is the debugging cost, which is paid by your own team. Obfuscated output means your error monitoring receives stack traces that point into generated code, and reproducing a bug locally means running the obfuscated build rather than the source. Teams that ship frequently and debug from production traces will feel this on every incident.

The third is scope. The tool takes code and rewrites it. It does not bundle your application, it does not tree shake, it does not minify for size in the way a dedicated minifier does, and it does not produce a smaller file. The topic list on the repository includes uglify, which places the project in the same family, and the difference in approach is the point: a minifier strips names and whitespace and keeps your structure, whereas an obfuscator deliberately rewrites the structure into something a reader has to work backwards through. If your goal is a smaller bundle, this is the wrong tool; if your goal is that your logic is expensive to lift, it is the right one.

There is also the third-party playground as a consideration. Using the web version means sending your code to a service you do not control, which is a different risk profile from running the npm package in your own pipeline, and for proprietary code the local package is the sensible default.

Release history and what the project is aiming at

The version history tells you where the project is pointing. The newest release is 2.1.3, published on 2026-06-17, and it is titled Defeating Static Analysis. Before it, 2.1.2 was called Updates and 2.0.1 was called Fixes, both from earlier in 2026.

Those release names are the interesting part. A tool that only renamed variables and mangled strings was aiming at a reader skimming your source. A release called Defeating Static Analysis is aiming at the automated tools that parse JavaScript, which is a harder and more technical goal, and it implies that the output is expected to survive analysis rather than just discourage a person reading it.

The repository is in good shape for that kind of work. The last push was on 2026-08-14, the repository is not archived, and the default branch is master. The root holds a CHANGELOG.md, a Migration.md, CONTRIBUTING.md, a CODE_OF_CONDUCT.md and the docs/ directory, and the test suite runs under jest with the experimental VM modules flag, with a coverage script that writes an HTML report. The migration file is the one to read before upgrading, since it is where breaking changes in a 2.x line are written down, and a tool whose output changes shape between versions is going to change the shape of your stack traces too.

The licence is MIT, which for a build tool that most teams run in CI rather than redistribute is the permissive end of the spectrum, with no copyleft obligation on the code you obfuscate with it.

Editorial conclusion

JS-Confuser fits a team that ships browser JavaScript, wants to slow down casual reverse engineering of the shipped bundle, and can live with harder debugging and a build step in the pipeline. It does not fit a team hoping to keep secrets in the client, since anything in the bundle can be recovered. Before you adopt it, obfuscate one module and measure the cost on your own debugging and stack traces, then check the migration notes, because the project ships a Migration.md and the newest release is 2.1.3 from 2026-06-17. The last push was on 2026-08-14 and the licence is MIT.

Frequently asked questions

How do I install and run JS-Confuser?

Install it with npm install js-confuser, then require the package and call the obfuscate function with your source and an options object. The result is a promise whose result holds the generated code in the code property, so a build step reads the file, calls obfuscate and writes the result back out.

What transformations does JS-Confuser apply?

The README lists variable renaming, control flow obfuscation, string concealing, function obfuscation, locks for domain and date, and a feature to detect changes to source code. The documentation also has separate pages for options and for presets, where a preset is a combination of those transformations.

What is the difference between a preset and the options in JS-Confuser?

The usage example passes a preset of high along with a target and a stringEncoding setting whose comment notes it is normally enabled. Presets are curated combinations of transformations with a name, while options are the individual settings, and the documentation lists them on separate pages.

Is JS-Confuser still being worked on?

The repository is not archived and the last push was on 2026-08-14. The newest release is 2.1.3 from 2026-06-17, titled Defeating Static Analysis, preceded by 2.1.2 Updates and 2.0.1 Fixes, and the root holds a CHANGELOG.md and a Migration.md.

What licence does JS-Confuser use?

The package is published under the MIT licence, and the LICENSE file is included in the published files alongside dist, README.md and package.json. The project also offers a browser playground, which is the version to think carefully about before pasting proprietary code into it.

Official sources

  1. License: MIT
  2. MichaelXF/js-confuser on GitHub
  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/michaelxf-js-confuser.svg)](https://hysenlabs.com/projects/michaelxf-js-confuser)

Was this useful?