@
How It Works Features Protection Compare FAQ Launch Tool →

Your JavaScript.Unbreakable.

Compile JS to real virtual machine bytecode, encrypt with multi-pass XOR, and embed a stack-machine interpreter. Your source becomes virtually impossible to reverse engineer.

Start Obfuscating See How It Works
soulvm.lol — compiler output
$ soulvm compile app.js --passes 3 --unicode --cff

Parsing AST via Acorn...
Compiling to bytecode (53 opcodes)...
✓ 847 instructions compiled
Running control flow flattening...
✓ 94 blocks shuffled & relinked
XOR pass 1 → rotate pass 2 → reverse-key XOR pass 3...
✓ Encrypted (key hash: 3FA2C8D1)
Injecting Unicode variable names...
✓ 48 identifiers replaced

✓ Done in 3.2ms · 312B → 9.1KB
Output: app.obf.js
53 VM Opcodes· Encryption Passes· Unique Shuffle· 200+ Unicode Vars· 0 Dependencies· 100% Browser-Based· PHP API Ready· Anti-Debug Built-in· 53 VM Opcodes· Encryption Passes· Unique Shuffle· 200+ Unicode Vars· 0 Dependencies· 100% Browser-Based· PHP API Ready· Anti-Debug Built-in·
How It Works

Four layers.
One output.

Every piece of your JavaScript goes through a real compilation and encryption pipeline before it ever reaches the browser.

01
AST Parsing

Your source is parsed into a full Abstract Syntax Tree using Acorn. Every expression and declaration is understood structurally before compilation.

02
Bytecode Compilation

The AST is compiled into a 53-opcode stack-machine instruction set. Arithmetic, closures, loops, and object creation all become compact opcodes.

03
Control Flow Flatten

Basic blocks are Fisher-Yates shuffled and all jump targets rewritten. The execution path becomes a maze with no obvious direction.

04
3-Pass Encryption

XOR → byte rotation → reverse-key XOR. Output is Base64-encoded and chunked. Decryption only ever happens at runtime inside the VM stub.

Features

Everything switched
on by default.

Real VM — 53 Opcodes
Not eval tricks. A full stack machine: PUSH, LOAD, STORE, CALL, CALLM, RET, JMP, JT, JF, OBJ, ARR, NEW, bitwise ops and more.
Control Flow Flattening
Basic blocks are shuffled randomly on every compile with rewritten jump tables. No two outputs look alike.
Unicode Variable Names
All VM stub identifiers become characters from a custom 75+ entry Unicode charset, reshuffled on every compile. Impossible to grep.
Anti-Debug Protection
A DevTools timing attack fires at load and every 2s. If debugger pauses for over 100ms, it triggers an infinite loop.
Dead Code Injection
Fake variables, unreachable branches, and void expressions are woven into the output to confuse static analysis tools.
Anti-Reverse Engineering Protection
A reverse-engineering attack fires at load and every 2s. If debugger pauses for over 100ms, it triggers an infinite loop.
// Opcode Table 53 total
PCPush constantSTACK
LDLoad local variableVAR
STStore to localVAR
GL/GSGlobal load / storeVAR
ADDPop 2, push a+bARITH
JMPUnconditional jumpFLOW
JT/JFJump if true / falseFLOW
CALCall function (n args)CALL
CMTCall method with thisCALL
MKFMake closure objectFN
OBJBuild object from stackOBJ
ARRBuild array from stackOBJ
GPR/SPRGet/set propertyPROP
NEWConstruct with newCALL
BAN/BORBitwise AND / ORBITS
Protection Layers

Six walls between your
code and the world.

LAYER 01
AST-to-Bytecode Compilation

Source code is completely destroyed as text. It becomes a serialized instruction list with no resemblance to the original JS.

Compiler
LAYER 02
Control Flow Flattening

Every function's basic blocks are shuffled randomly. The logical flow becomes a non-linear maze of JMPs with no obvious entry or exit.

CFF
LAYER 03
3-Pass XOR Encryption

Bytecode is XOR'd, byte-rotated, then XOR'd again with reversed key. Decryption only ever happens inside the runtime stub.

Crypto
LAYER 04
Unicode Identifier Substitution

Every variable in the VM stub is replaced with characters from a 75+ entry Unicode charset, reshuffled on every compile.

Rename
LAYER 05
Anti-Debug Timing Attack

A debugger-based timing check runs at load and every 2 seconds. If DevTools pauses for over 100ms, the tab locks in an infinite loop.

Runtime
LAYER 06
Dead Code Noise

Random variables, unreachable branches, and void expressions are woven into the output to confuse static analysis tools.

Noise
Before & After

See the difference.

Before — readable sourceEXPOSED
// Business logic, fully readable
const SECRET = "ENT-2024-XXXXXX";
const VALID = ["ENT-2024-XXXXXX","PRO-2024-YYY"];

function isLicensed(key) {
  return VALID.indexOf(key) !== -1;
}

class PricingEngine {
  constructor(base) {
    this.prices = base;
    this.discounts = {gold:0.30,silver:0.15};
  }
  calculate(sku, tier, qty) {
    var base = this.prices[sku] || 0;
    var disc = this.discounts[tier] || 0;
    return base * (1 - disc) * qty;
  }
}
After — VM protectedPROTECTED
(function(){
var __ʻʼ=1243819;
var __ᴵᴵ=function(){return 8823;};
if(false){throw new Error('0x3fa2e1');}
var __ˑˑ=["U2Fsd...","GxcR1..."].join('');
var __ʾʿ=[12,44,7,91,23,...];
var __ˆˈ=function(s){
  var __ʻʽ=atob(s),__ᵎᵎ=[];
  for(var __ᵢᵢ=0;__ᵢᵢ<__ʻʽ.length;__ᵢᵢ++)
    __ᵎᵎ.push(__ʻʽ.charCodeAt(__ᵢᵢ));
  /* 3-pass decrypt... */
  return JSON.parse(__ᵎᵎ.map(...));
};
/* VM: 200+ lines switch/case */
__ˊˋ(__ˆˈ(__ˑˑ));
})();
FAQ

Common questions.

Yes. The VM faithfully executes all compiled opcodes — arithmetic, loops, conditionals, function calls, object creation, prototype methods, and more.
Most ES2020 features: var/let/const, functions, arrow functions, classes, for/while/do-while loops, switch, try/catch, template literals, destructuring, spread, ternary, logical operators (&&, ||, ??), and all binary/unary operators.
The compiler identifies every basic block — a straight-line sequence ending in a jump or return. These are Fisher-Yates shuffled, all relative jump offsets rewritten, and trailing JMP instructions added.
No. The Backend never saves your source code.
Yes. The VM stub uses globalThis with fallbacks to window and global, making it compatible with both browser and Node.js environments.

Start protecting
your code now.

Free. No account required. Runs entirely in your browser.