If you ship a Phaser, PixiJS, Cocos Creator (web), or Construct game, players already have your scripts. Opening DevTools → Sources is enough to read economy formulas, unlock flags, and anti-cheat stubs. Reskins on portals are not theoretical — they happen.

This post is a practical playbook: what to protect, what to leave alone, why VM-based protection has a real performance cost, and how to wire a selective pipeline. I'll use VEILVM (a browser JSVMP workspace I built) as the example tool, but the selective-protection idea applies even if you use something else.

The threat model (be honest)

Anything that runs on the player's device can eventually be reversed. Obfuscation and VM protection raise time and cost; they do not make code unbreakable. For competitive integrity you still want:

  • Server-authoritative scores / inventory when cheating matters
  • Purchase validation on your backend
  • Not shipping secrets (API keys, private matchmaking tokens) in the client at all

Client-side protection is for the other problem: clone and reskin. Someone who wants your drop tables, soft-currency curve, or paid unlock logic should have to work for it.

Why "just minify" and classic obfuscators fall short

Minification shrinks identifiers. Classic obfuscators (javascript-obfuscator and friends) add string encoding, control-flow flattening, dead code. The output is still ordinary JavaScript. Beautifiers and public undo scripts exist for many of those patterns.

JSVMP (JavaScript Virtual Machine Protection) takes a different route:

  1. Parse selected source into an AST
  2. Compile it to a custom bytecode
  3. Ship a small interpreter that runs that bytecode

Attackers usually have to reverse or instrument the VM before they recover the original logic. Higher reverse-engineering cost — and higher size/CPU cost for you.

The rule that saves your frame budget

Do not put the whole game into the VM.

VM-protected code runs noticeably slower than native JS. If you wrap your render loop, physics step, or per-frame update, you will drop frames and players will blame "the game," not "the obfuscator."

Protect (small, high-value modules):

  • Purchase / entitlement checks
  • Save-game integrity / anti-tamper stubs
  • Level unlock gates
  • Economy, drop tables, damage formulas you don't want mirrored

Leave alone:

  • Phaser / Pixi / Cocos / engine runtime
  • Scene wiring and UI glue
  • Anything that runs every frame

Treat JSVMP like a scalpel, not a spray can.

A selective workflow that fits a bundler

src/ economy/ dropTable.js ← protect this entitlement.js ← protect this game/ mainLoop.js ← never protect render.js ← never protect dist/ economy.protected.js 

Build sketch:

  1. Develop and playtest with plain modules.
  2. Before production bundle, run only the critical files through a JSVMP step.
  3. Import the protected outputs from the rest of the game as usual.
  4. Measure FPS and load time on a mid-tier laptop and a phone — not only on your gaming PC.

Example: protect one helper, not the scene

// src/economy/dropTable.js (protect this file only) export function calcDropRate(enemyLevel, luck) { return Math.min(0.85, 0.05 + enemyLevel * 0.01 + luck * 0.002); } 
// src/game/battleScene.js (leave readable / lightly minified) import { calcDropRate } from "../economy/dropTable.js"; // or the protected build path export function onEnemyDefeated(enemy, player) { if (Math.random() < calcDropRate(enemy.level, player.luck)) { grantLoot(enemy.lootTable); } } 

In CI you can call a hosted API instead of clicking the browser UI. VEILVM Pro exposes:

curl -X POST https://vm.ldvmp.com/api/v1/obfuscate \ -H "Authorization: Bearer $VEILVM_API_KEY" \ -H "Content-Type: application/json" \ -d '{"code":"<contents of dropTable.js>","mode":"performance"}' 

Keep the API key in CI secrets. Never commit it.

What "good" looks like after protection

Check Pass criteria
Game still boots No missing exports from protected modules
FPS on target device Within ~your usual budget on hot paths (those paths should not be VM'd)
Bundle size Only the protected files grew a lot; engine untouched
DevTools glance Economy / entitlement no longer readable as normal JS

If FPS tanks, you protected too much — peel the VM off the frame loop first.

Engine notes (JavaScript only)

Stack Fit Note
Phaser / PixiJS / Babylon.js (web) Strong Protect logic modules before Vite/webpack pack
Cocos Creator (web / JS build) Strong Same idea on exported JS
Construct HTML5 Good Focus on custom JS you care about
RPG Maker MV / MZ plugins Strong Plugins are JS; protect core logic, keep @param usable
Unity / Roblox / Lua FiveM scripts None Not JavaScript — wrong tool

Also skip channels that ban obfuscated submissions (browser extension stores, Greasy Fork). Shipping your own game build is a different story from uploading an obfuscated userscript.

Try before you rebuild the pipeline

VEILVM runs in the browser:

  • 3 free runs a day, no sign-up, up to 5 KB per file (resets 00:00 UTC)
  • Free account: 20 runs/month, 30 KB/file
  • Pro: $9/mo or $79/year — 1,000 runs/month, no plan-level file-size cap, 2 concurrent jobs, HTTP API

Paste one entitlement or drop-table module first. If size and behavior look fine in your target browser, wire the same step into CI.

Takeaways

  1. Client protection raises clone cost; it does not replace server checks.
  2. Selective protection — billing, saves, levels, numeric cores — not the render/physics loop.
  3. Expect size and CPU cost on whatever you put in the VM; measure on real devices.
  4. Automate only the critical files in CI so every release stays consistent.

If you try this on a Phaser or Cocos web build, I'd love to hear what you protected and what you deliberately left alone — that trade-off is the whole game.