Runtime performance

Token cost measures what it costs to build; this page measures what you get. Every implementation was rebuilt in Release configuration from its preserved source tree and profiled on one fixed simulator (iPhone 17 Pro) — no new agent trials were needed, because performance belongs to the artifact, not the agent’s path to it. Raw per-app JSON is in the repository ↗.

CorrectionAugust 2, 2026

The app-size finding was wrong, and reversed on re-measurement

As first published, size came from the Release-iphonesimulator .app directory, which carries an x86_64 slice that never ships and full symbol tables. That penalised LynxJS far more than NativeScript, because its DevTool/DebugRouter stack was counted fat and unstripped at 23.6 MB; in a device archive that stack is 5.3 MB. Re-measured as unsigned arm64 device archives, LynxJS is smaller than NativeScript rather than 50% larger — the conclusion reverses, and the write-up no longer claims footprint as NativeScript’s runtime edge. What survives is the narrower finding that the Sparkling template ships DevTool in Release at all; only its weight was overstated. Token results, memory, launch, and CPU are unaffected. The NativeScript figure is a single reference build: this study predates study-scoped trial branches and its per-trial NativeScript trees were later overwritten by a subsequent run that reused the same branch names. The one surviving spec-v1.0 tree measures 51.1 MB, matching all 8 NativeScript archives in the companion Expo study exactly, so the substitution does not move the number.

FigureAs publishedCorrected
NativeScript bundle93.3 MB51.1 MB
LynxJS bundle140.2 MB26.1 MB
LynxJS DevTool stack23.6 MB5.3 MB
Headline gap+50%−49%
Bundle on disk
−49%

26.1 vs 51.1 MB; 5.3 MB of the LynxJS side is DevTool.

Idle memory
+34%

288 vs 215 MB RSS; footprint 48 vs 38 MB.

Launch to active
tie

316 vs 317 ms; launch-burst settle +7% on Lynx.

Release-buildable as-written
5/5 · 4/5

All NativeScript apps built untouched; one Lynx app fails Release compilation.

01Why Release builds, and which apps

Debug builds carry development-only weight — unoptimized code and, on Lynx, the DevTool/DebugRouter attach path — so they are never measured. Each trial’s final source tree (preserved as a git branch) was rebuilt with -configuration Release and archived. Measuring all implementations rather than one per side turns implementation variance into data: across independently agent-built apps, device-archive size does not vary at all — every lynxjs app measures 26.1 mb — while cold launch varies <6 ms and idle rss <8 mb. runtime characteristics are a property of the framework, not of how the agent wrote the app.

The excluded app: 5/5 NativeScript implementations built in Release untouched; 4/5 LynxJS did — main-lynx-5 fails Release compilation as-written ("Multiple commands produce sample.wav": the agent wired the audio asset through two copy mechanisms, tolerated by Debug, rejected by Release). It fails identically in a device archive. Excluded and disclosed; patching it would invalidate the artifact.

02What ships — bundle composition

Both bundles are almost entirely runtime, and NativeScript ships the heavier one: an 11.8 MB executable with V8 metadata compiled in, plus a 37.1 MB NativeScript.framework. Lynx ships a 0.8 MB executable over Lynx.framework (14.4 MB), PrimJS (2.1 MB) and its supporting pods. 5.3 MB of the Lynx side is the DevTool/DebugRouter stack the Sparkling template includes even in Release (hatched below; quantified separately so it can be subtracted) — smaller than it looked in a simulator build, but still shipped.

Size is an unsigned arm64 device archive per app (xcodebuild archive, generic/platform=iOS), which runs the same strip and postprocessing an App Store build gets. Uncompressed and before App Store thinning, so these are upper bounds on download size. The LynxJS figures are the median of the same 4 apps measured below; the NativeScript figure is a single reference build, because this study predates study-scoped trial branches and its per-trial NativeScript trees were later overwritten — one spec-v1.0 tree survives. The companion Expo study justifies the substitution: all 8 NativeScript archives there measured 51.1 MB without variation. Memory, launch, and CPU below come from simulator builds and are not comparable to these figures.

NativeScript51.1 MB
executable 11.8 MBframeworks 39.3 MB
LynxJS26.1 MB
executable 0.8 MBframeworks 20.0 MBDevTool 5.3 MB (hatched)
03Memory, launch, idle CPU
Release, simulator (median)NativeScript (n=5)LynxJS (n=4)Δ
Idle RSS215 MB288 MB+34%
Idle physical footprint38 MB48 MB+26%
Cold launch → foreground-active316 ms317 ms~equal
Launch-burst settle (CPU quiesce)1.53 s1.64 s+7%
Idle CPU0.0%0.0%equal

5 cold launches per app, terminated between runs; launch timing from just before invocation to the OS foreground-active marker in the system log, CPU-quiesce fallback recorded alongside. Idle sampled 10×1 s after a 12 s settle. Both frameworks idle clean — the Release DebugRouter is dormant, not polling.

04Using the platform APIs — tap to result
Feature path (median ms, range)NativeScriptLynxJSVerdict
Nav → Health screen rendered1,903 (1,7842,216)1,785 (1,7691,819)~tie, Lynx tighter
7-day HealthKit query → rows rendered1,059 (1,0331,081)1,085 (1,0701,113)tie — 2% apart
Log 500 steps → list refreshed643 (385647)447 (441447)Lynx ~30% faster
Nav → Transcribe screen rendered1,874 (1,7062,188)1,758 (1,7331,782)~tie, Lynx tighter

Measured by a generic external UI-test driver tapping every app through its real flows by the spec-pinned labels (agent-built apps untouched), 3 iterations per app, fresh launch per iteration, permission sheets auto-granted. The heavy read path is a dead tie — the OS API dominates. Lynx takes the write→refresh round trip by ~30%, though the single fastest app overall was a NativeScript one (385 ms), so refresh strategy matters as much as framework. No latency metric materially favors NativeScript, and app size does not either once measured on a device archive — its remaining runtime edge is idle memory.

Transcription latency is simulator-blocked identically for both frameworks: SFSpeechRecognizer server recognition rides Siri infrastructure that iOS Simulators lack. All 27 driver iterations across all 9 apps failed to initialize the recognizer, and every app correctly surfaced the error in its status line per spec. It is a physical-device metric — each app displays "Completed in N.N s" on screen.

05JS↔native interop microbenchmarks — the dial-to-native test
Scenario (identical both sides)NativeScriptLynxJSRatio
Pure-JS compute — 2M-iteration math loop57 ms271 ms4.8×
JSON round-trip × 500 (100-key object)4 ms17 ms4.3×
Native call latency — 1,000 sequential calls~1 µs/call~130 µs/call~130×
10,000 native calls in flight5 ms1,191 ms238×
1 MB string across the boundary, per call1.0 ms2.0 ms
Checksum 1 MB — pure JS74 ms444 ms
Checksum 1 MB — native via interop3 ms9 ms

Hand-written bench apps (separate from the agent-built corpus) on the interop-bench branch of each framework repo: identical scenario code both sides, an equivalent native fixture each (ObjC class via direct binding on NativeScript; authored Swift LynxContextModule on LynxJS), self-timed in-app. Every scenario emits a check value that must match across frameworks — all 8 do. Medians of 3 interleaved Release runs plus a verification run rebuilt from the committed sources.

06Scope limits, stated plainly
  • iOS Simulator on one Mac: absolute numbers are not device numbers — only the comparative readings transfer, and they share host, runtime, and interleaved scheduling.
  • No scroll/FPS metric: the spec’d app has no scroll-stressing surface, and Lynx’s multithreaded-rendering pitch targets exactly that kind of load. That claim is explicitly untested here — not quietly folded in.
  • JS↔native bridge micro-latency is not separately instrumented (that would require modifying the agent-built apps); it shows up indirectly in the tap-to-result timings, where it proved immaterial.
← Back to resultsMethodology →