6.4% smaller than original
smallest upstream per module · 46,660 Bposthog-js@1.435.8 · seven submodules · LilScript port
PostHog,
compiled.
Seven submodules of posthog-js@1.435.8 rewritten in LilScript:
exception capture, browser and device detection, autocapture utilities, feature-flag responses,
the metrics client, UUIDv7 ids and request gzip. Each one is compared on its own with
PostHog’s own build of the same code, and with esbuild and Oxc.
17,234 B PostHog’s build → 16,953 B LilScript · Brotli-11, all seven
Raw, gzip and Brotli each use a separate LilScript compilation targeting that objective. Each submodule is compared on its own; the totals add the seven.
Pick a module
Size and speed,
module by module.
Each tab is one submodule; the first adds up all seven. Under it: the size of every build for raw, gzip and Brotli against PostHog’s own build, esbuild and Oxc, the speed in Chromium against PostHog’s build, the build time and the upstream tests — every measured file linked.
All seven submodules
Totals of the seven submodules, each measured on its own. For each codec, a module’s original is its smallest upstream build (PostHog’s build, esbuild or Oxc); the LilScript side is the build compiled for that codec.
packages/{core,browser-common}/src/…
Portsrc/posthog/…
BehaviourAll 1,076 of 1,076 upstream tests that pass against PostHog’s build pass against LilScript’s (21 files). 38 more fail against both: their mocks reach private names PostHog’s build mangles.
Cells21 of 21 smaller
All seven submodules · size
Three objectives, measured separately.
1.6% smaller than original
smallest upstream per module · 19,045 B1.6% smaller than original
smallest upstream per module · 17,234 B| Compiler objective | LilScript | Original minified | Original minifier | Difference |
|---|---|---|---|---|
| Raw | 43,673 B | 46,660 B | smallest upstream per module | 6.4% smaller |
| gzip-9 | 18,734 B | 19,045 B | smallest upstream per module | 1.6% smaller |
| Brotli-11 | 16,953 B | 17,234 B | smallest upstream per module | 1.6% smaller |
Each LilScript row comes from a separate compilation targeting that row’s objective. Raw is uncompressed JavaScript; gzip uses level 9; Brotli uses quality 11 and window 22. For each metric, the original is the smallest result among the minifiers below. No JavaScript minifier runs after LilScript.
Every artifact, measured with all three codecs
| Artifact | Raw | gzip-9 | Brotli-11 |
|---|---|---|---|
| LilScript · Raw objective | 43,673 B | 19,345 B | 17,426 B |
| LilScript · gzip-9 objective | 45,101 B | 18,734 B | 16,990 B |
| LilScript · Brotli-11 objective | 45,258 B | 18,872 B | 16,953 B |
| Original · PostHog’s build Rolldown 1.2.12 + Terser 5.51.2 | 46,987 B | 19,045 B | 17,234 B |
| Original · esbuild 0.28.2 | 67,055 B | 28,452 B | 25,707 B |
| Original · Oxc (Rolldown) Rolldown 1.2.12 | 50,234 B | 19,991 B | 18,040 B |
Seven submodules of posthog-js, each bundled from its TypeScript with everything it imports and compared on its own; the totals add them. Upstream: posthog-js@1.435.8. No external runtime imports. CSS, fonts, type declarations and source maps are outside these JavaScript sizes.
Measurements, hashes and settings ↗ · Commands and build receipts ↗
All seven submodules · speed · Chromium 139.0.7258.5
Speed in the browser.
| Module | Workload | PostHog’s build | LilScript | Time | 95% interval | Verdict |
|---|---|---|---|---|---|---|
| Exception capture | Importdynamic import from a fresh Blob URL: parse, compile, evaluate | 1.565 ms | 1.765 ms | +11.9% | +8.5% … +16.9% | slower |
| Exception capture | build-properties29 inputs through the properties builder: real Chromium errors with causes, an AggregateError, a DOMException, error and rejection events, strings, objects | 2.635 ms | 2.648 ms | +0.5% | −0.5% … +1.5% | no clear difference |
| Exception capture | parse-stacks11 Chrome, Firefox and Safari stack strings through the parser | 1.714 ms | 1.758 ms | +2.5% | +1.9% … +3.5% | slower |
| Exception capture | exception-steps150 exception steps into the 32 KB buffer | 0.955 ms | 0.915 ms | −4.7% | −5.9% … −3.5% | faster |
| Browser, OS and device detection | Importdynamic import from a fresh Blob URL: parse, compile, evaluate | 1.280 ms | 1.245 ms | −4.2% | −6.6% … +0.2% | no clear difference |
| Browser, OS and device detection | corpusOS, browser, device, device type and version for the 606 user agents in posthog-js’s tests | 4.030 ms | 3.593 ms | −11.3% | −12.5% … −10.0% | faster |
| Browser, OS and device detection | traffic-mixthe same per-event sequence for 200 events from a modern browser mix | 0.921 ms | 0.782 ms | −15.4% | −15.7% … −14.6% | faster |
| Autocapture DOM utilities | Importdynamic import from a fresh Blob URL: parse, compile, evaluate | 1.540 ms | 1.560 ms | +4.5% | −0.9% … +7.8% | no clear difference |
| Autocapture DOM utilities | click-pipeline43 clicks on a realistic app page through posthog-js’s autocapture flow, with the dead-click and rage-click checks | 11.825 ms | 11.685 ms | −1.1% | −2.7% … −0.0% | faster |
| Autocapture DOM utilities | text-scrubscrubbing page text, attributes and 12 network bodies | 0.537 ms | 0.540 ms | +0.5% | −2.3% … +3.0% | no clear difference |
| Feature flag responses | Importdynamic import from a fresh Blob URL: parse, compile, evaluate | 1.098 ms | 1.127 ms | +3.3% | −4.0% … +9.9% | no clear difference |
| Feature flag responses | flags-v2-readsa 64-flag /flags response handled as the client does: normalize, values, payloads, reads, overrides, the flag-called event | 0.521 ms | 0.532 ms | +1.4% | +0.5% … +2.5% | slower |
| Feature flag responses | flags-v1-storagerebuilding stored flags in the older format | 0.172 ms | 0.153 ms | −11.6% | −12.4% … −7.8% | faster |
| Metrics client | Importdynamic import from a fresh Blob URL: parse, compile, evaluate | 1.345 ms | 1.525 ms | +16.6% | +7.5% … +21.2% | slower |
| Metrics client | record-drain1,000 samples (histograms, counters, gauges), then drainWindow() | 1.614 ms | 1.600 ms | −0.8% | −5.2% … +0.3% | no clear difference |
| Metrics client | record-flush200 samples, then await flush() | 0.546 ms | 0.482 ms | −11.0% | −13.4% … −8.3% | faster |
| UUIDv7 ids | Importdynamic import from a fresh Blob URL: parse, compile, evaluate | 1.203 ms | 1.212 ms | −3.1% | −6.0% … +3.0% | no clear difference |
| UUIDv7 ids | generate1,000 uuidv7() calls | 0.782 ms | 0.786 ms | +1.0% | −0.2% … +1.9% | no clear difference |
| UUIDv7 ids | timestamp1,000 uuid7ToTimestampMs() calls | 0.271 ms | 0.274 ms | +1.0% | −1.4% … +2.8% | no clear difference |
| Request compression | Importdynamic import from a fresh Blob URL: parse, compile, evaluate | 1.102 ms | 1.110 ms | +1.8% | −2.5% … +9.8% | no clear difference |
| Request compression | event-batcha 30-event batch (61 KB) | 2.414 ms | 2.412 ms | +0.2% | −1.7% … +1.3% | no clear difference |
| Request compression | replay-snapshota 563 KB replay snapshot | 14.260 ms | 14.233 ms | −0.1% | −0.6% … +0.4% | no clear difference |
Each sample is a fresh browser context on a local, cross-origin-isolated page; PostHog’s build and LilScript’s alternate in ABBA order, 40 samples per module. A time is the median of 15 repetitions after 5 untimed ones; the change is the median of the paired differences with a 95% bootstrap interval, and a verdict needs that interval to exclude zero. Both builds first run the same inputs and must give identical output. A control — PostHog’s build against an identical copy of itself — flagged 3 of 37 rows, so changes of one or two percent are within noise. Import is slower where a module defines most of its functions as top-level arrows, which V8 parses more slowly in module code.
All seven submodules · same machine · sequential builds
Build time.
| Output objective | LilScript compilation | Original bundling + minification | Original minifier |
|---|---|---|---|
| Raw | 8.05 s | 0.712 s | smallest upstream per module |
| gzip-9 | 12.95 s | 0.870 s | smallest upstream per module |
| Brotli-11 | 26.50 s | 0.870 s | smallest upstream per module |
One fresh output build per objective and minifier, run sequentially. LilScript compilation includes optimization and objective scoring with persistent caches disabled and one worker. Original time includes Node startup, esbuild bundling of installed upstream ESM, and the named minifier. It excludes building the upstream package from its original TypeScript sources. Dependency installation, tests and final file compression are excluded on both sides. These are observations on a shared machine, not a build-speed guarantee.
AMD EPYC 7763 64-Core Processor · 8 logical CPUs · 15.6 GiB RAM · Linux 7.0.0-1014-azure · Node v20.19.0. Measured 2026-10-04. Exact commands and compiler identity ↗
The compiled runtime, in the browser
Live
lab.
Left is the input. Right is what the selected build returns — LilScript’s or PostHog’s — and whether the other build returns the same. Both builds of the module load in this page.
The lab runs with JavaScript enabled.
posthog-js@1.435.8 · LilScript d8f79ed5
Same behaviour.
Smaller.
Not affiliated with PostHog. posthog-js is MIT-licensed; its license and notices ship with this site. Upstream tests are counted as the behaviour gate counts them: every test that passes against PostHog’s bundled, mangled build must pass against LilScript’s.