scriptc: Compiling TypeScript to Native Binaries — Fast Startup, Slow Computation
Two numbers frame this story. A CLI tool compiled with scriptc starts in a median of 1.78 ms; the same CLI takes 61.78 ms on Node and 21.29 ms on Bun. That's over 30x faster startup — official benchmarks. The other number, from a developer's real-world test on Hacker News: byte-array computation — scriptc's most favorable scenario — runs about 7.5x slower than Node 24. Both numbers are true, and they belong to the same project.
The project
scriptc, from Vercel Labs, Apache 2.0 licensed, created July 22, 2026, compiles ordinary TypeScript into native executables: no Node, no V8, no JavaScript engine inside the binary. The pipeline follows real compiler practice — the official TypeScript compiler handles parsing and type checking, code is converted to a typed intermediate representation, and can then be emitted as readable C, LLVM IR, assembly, object files, a native executable, or WebAssembly via WASI. Install with npm install -g scriptc.
Three-tier design
Its cleverest design decision is classifying every piece of syntax into one of three tiers:
1. Clean, type-safe code compiles directly to static machine code.
2. npm dependencies and any-typed code, when compiled with --dynamic, runs inside an embedded ~620 KB quickjs-ng engine — the author calls it the "dynamic island"; code inside is compatible but slow.
3. Code that can neither be compiled nor justifiably placed in the island is rejected at compile time with an SC error code and rewrite suggestions.
A coverage command tells you exactly which operations in your project fall into which tier. The honesty lies in clearly priced boundaries: no silent fallback, costs documented.
The cost: Hono's ledger
A server written with the Hono framework must use --dynamic; the result is 62% of code inside the QuickJS island and throughput of 18.4K requests/sec — versus Bun's 70.5K. Whether code lands in the island isn't a framework-specific accident; it's a floor for the whole ecosystem. JavaScriptCore co-creator Filip Pizlo analyzed TS repositories in the HN thread: 79.5% explicitly use any, and one-third of inferred types fall back to any. Roughly four out of five TypeScript codebases are destined for the slow island; only a fifth fits the static tier.
Pizlo's broader critique
- All numbers in scriptc are represented as floating point; integer inference is deferred — "inferring integers is half the problem of making JS fast."
- QuickJS is "absurdly slow"; he quipped mediocre engines are used "because of the name containing 'quick.'"
- Reference-counted memory management "guarantees unusably slow" performance.
- Simon Willison compiled the official fibonacci example to C and found
static double sc_f_fib(double n)— inputs and outputs all doubles — making the accusation visible.
The AI-generated code question
Willison noted the repo accumulated 918,000 lines in one week, with a single commit of 802,000 lines — produced by coding agents. scriptc is an extreme case of agent-driven development: a compiler stacked up in three weeks by an agent team. The speed is real; architectural review was absent. Pizlo's criticism is essentially one human expert spending his own bandwidth auditing 918K lines of AI output.
The trust ledger
Vercel Labs is a lab, and labs have precedent: zerolang, launched with fanfare in May, had 1,000+ commits in June, then nearly stalled after July (one commit per month for three months). scriptc's survival curve looks different so far — full-speed development in October, the last 100 commits all within the current month, stars climbing from 4,900 at writing to 6,000 — but the experimental-project disclaimer remains. One thing it did right: differential testing, byte-for-byte comparison of stdout, stderr, and exit codes against Node. Behavioral consistency is tested, not claimed.
The bottom line
For CLI tools, edge functions, and container cold starts — scenarios valuing startup speed and memory — it genuinely wins: Remo Jansen measured 3.6 ms cold start vs Node's 48.9 ms, 1.9 MiB idle memory, and a single 370 KB file with zero runtime dependencies. For compute-intensive workloads, the gap is currently 7.5x, rooted in the absence of integer inference — not something tuning can fix. To see the boundary yourself, feed any any-typed TypeScript to the official playground and watch it push your code into the 620 KB island — that single output says more than any benchmark report.
> Note: This is a translated and lightly edited version of a Chinese forum post from zhichai.net.