AfterPack performance: measured speed and size

What AfterPack costs a build: measured obfuscation time on a 185 KB bundle, the wasm build's time and download size, and the output size multiplier each preset produced.

Every number on this page was measured, not estimated. Unless a table says otherwise, the machine was an Apple M2 Max, 32 GB RAM, macOS 26.6.2. Nothing here is a comparison against another tool.

The fixture behind most of it is next16_framework_runtime: 185 KB of the framework runtime a real Next.js 16 build ships. It is one already-minified production bundle, which is the shape AfterPack actually runs on.

Build time

Measured on 2026-08-31, on the machine above.

What ranInputTimeThroughput
The npm package, on the 185 KB bundle185 KB225 ms0.81 MB/s
The engine on its own, same bundle185 KB210 ms0.86 MB/s
The npm package, on a 490 KB multi-file build490 KB615 ms0.78 MB/s

The npm row is the one to plan against. It is the published package doing the work the way a plugin or npx afterpack@latest does it, so it includes the cost of getting your code into the engine and back out. The engine row is the same work with that cost removed, and the 15 ms between them is what the packaging adds.

Peak memory on the engine row was 41.7 MB above baseline for a single 185 KB file. The 490 KB multi-file run held 310 MB at its high-water mark.

Obfuscation is a post-build step, so this time is added to your build, not to your users' page loads. A 490 KB build in about six tenths of a second is the shape of the cost.

The wasm build

@afterpack/wasm is the same engine compiled to WebAssembly, for a Cloudflare Worker, for Node, and for the playground on this site.

MeasureValue
Time on the 185 KB bundle, in Node431 ms
CI budget on that fixture600 ms
Download, raw4.65 MiB
Download, gzipped1.41 MiB

The budget is enforced: a CI check runs the engine five times on that fixture at complexity 8 with the Protection Map on, and fails the build if the median passes 600 ms.

Wasm costs about twice what the native build costs, and that is a deliberate trade the other way round: an earlier build optimised for size came out 39% smaller gzipped and 2.1x slower, which pushed a real 66-file build past the cloud engine's deadline. Speed won.

The download happens once per Worker isolate or per browser tab, not once per build.

A laptop core and a Worker isolate are not the same machine. An isolate has less CPU, a colder cache, and a per-request CPU limit, so the same input takes longer there than this table says. Measure on the platform you will run on before you size a budget against these numbers.

Output size

Obfuscation adds bytes. Measured across the fixture set, per preset, on gzipped output against the same file minified:

PresetGzipped output multiplier
minify1.32x to 1.41x
light1.92x to 2.39x
medium1.61x to 2.70x
hard2.10x to 3.31x
extreme2.91x to 4.37x

Each range is the spread across the fixtures, not a confidence interval. medium overlaps light because the two presets respond differently to different code shapes.

This is a different measurement from the inflation.max budget on Presets, which caps raw bytes against your input. Compare a number to the budget only when both are measured the same way.

Small files come out proportionally larger than any of these, because every output carries the engine's fixed runtime overhead. Read a ratio on a real bundle.

Next

  • Presets: the size budget each preset sets, and what raising one buys.
  • Build & CI: where the step belongs in a pipeline.
  • Cloudflare Workers: running the wasm build in an isolate.
  • Playground: the wasm build, running in your browser, timing itself.