Creuto is now an OpenAI Select Partner Read More

Custom Software Development

scriptc compiles TypeScript to native: fast, with caveats

scriptc compiles TypeScript to native binaries: 1.78ms startup against Node's 61.78ms, 1.9MiB idle, about 7.5x slower compute, and explicitly experimental.

scriptc compiles TypeScript to native: fast, with caveats

A framework-free HTTP server written in TypeScript, compiled to a native binary with no Node and no JavaScript engine inside it, idles at 1.9MiB. That is the number worth remembering about scriptc, Vercel Labs' new TypeScript to native compiler. The startup figures are just as stark, and so are the caveats: it is explicitly experimental, and it will refuse to compile most of what is in your node_modules.

scriptc is Apache-2.0 licensed, its repository was created on 22 July 2026, and it has passed 5,000 stars as of 27 September 2026. It uses the real TypeScript compiler for parsing and type checking, lowers the program to a typed IR, and emits readable C, textual LLVM IR, assembly, relocatable objects, native executables, or WebAssembly via WASI Preview 1.

The startup numbers, and what they actually measure

InfoQ reported a benchmark of scriptc 0.0.16 against Bun 1.3.12 and Node 24.18.0 with median CLI startup of 1.78ms for scriptc, 21.29ms for Bun and 61.78ms for Node.

RuntimeMedian CLI startupWhat it carries
Node 24.18.061.78msV8, the Node runtime, your dependency tree
Bun 1.3.1221.29msJavaScriptCore and the Bun runtime
scriptc 0.0.161.78msA small native runtime; no Node, no JavaScript engine

Those are process startup medians for a CLI, not throughput. Compute tells the opposite story. One developer on Hacker News, quoted by InfoQ, measured their byte-array workload running about 7.5 times slower than Node 24 even after model-assisted, scriptc-specific optimisation — while the same executable started 12 times faster (1.5ms against 18.6ms), used 72 times less memory (2.5MiB against 181MiB), and shipped as a single 370KB binary with no runtime dependencies.

The same InfoQ benchmark found Hono required --dynamic, which pushed 62% of the server into the embedded QuickJS island and cut throughput to 18.4k requests per second against Bun's 70.5k. That is the trade in one line: the moment you need a real framework, the numbers that made scriptc interesting stop applying.

Where compiling TypeScript to native actually pays

Startup and memory dominate in a narrow, real set of workloads, and compiling TypeScript to native is worth evaluating in exactly those.

  • Developer CLIs invoked constantly. A pre-commit hook, a lint wrapper, a codegen tool. 60ms of Node startup per invocation is invisible once and intolerable in a loop.
  • Short-lived jobs and cold-start-sensitive functions. Anything where the process lives for less time than it takes a JavaScript runtime to boot.
  • Edge and constrained targets. scriptc emits WASI Preview 1 modules, and a 1.9MiB idle footprint changes what fits.
  • Single-binary distribution. A 370KB executable with no runtime dependency is a materially different shipping story from "install Node first".

Cold start is a real cost line, not a benchmark trophy — we have written about the production issues worth learning early on edge runtimes for the same reason. What scriptc does not help with is anything CPU-bound, long-lived, or dependency-heavy. A request-handling service that stays warm gains nothing from a 60ms saving it pays once and loses 7.5x on compute forever.

What scriptc will not compile

The compiler's own limitations page opens with "Honesty is the product", and it earns that. Everything unsupported is either a compile error with an SC code and a rewrite hint, or a numbered divergence — nothing fails silently. scriptc coverage reports, per statement, how much of your program compiles statically.

Constructs land in one of three tiers, as InfoQ summarises: compiled statically by default; run dynamically inside an embedded quickjs-ng engine of roughly 620KB when you pass --dynamic for npm packages and any-typed code; or rejected at compile time. The dynamic tier is the one to watch, because it is where the performance argument leaks away.

The fences are specific rather than vague. Record shapes are exact structs, so passing {a, b} where {a} is expected is rejected as SC2002. Union operations with no common per-arm ABI — reading u.length on string | string[] — need narrowing first. Object-to-primitive loose comparisons stay fenced because a custom valueOf can execute arbitrary code. On the WASI target, network sockets and fetch, child processes, OS signals and filesystem watching all fail before linking with SC3002.

One developer quoted by InfoQ ran coverage against every project they had locally and got hundreds of errors on each, concluding that if you have to write from scratch without third-party libraries you may as well reach for Rust, Go or Zig. That is the strongest form of the counter-argument and it is a fair one — it is also exactly what the three-tier design predicts for an existing codebase.

Deliberate divergences from Node you should read first

scriptc is not a drop-in Node replacement, and some differences are by design rather than gaps. Strings are stored as UTF-8. Memory is reference counted rather than garbage collected. Object.keys reports declaration order. process.argv[0] is "scriptc". Reference counting in particular is a semantic change, not a tuning knob: cyclic structures behave differently from a tracing collector, and that is the kind of thing that shows up in production rather than in a benchmark.

The toolchain has requirements too. The compiler needs Node.js 24 or newer and installs with npm install -g scriptc; source-level outputs (--emit=ir|c|llvm) need only Node, while ordinary executable builds need a platform linker driver and SDK. Cross-compiling to WASI requires Zig on your PATH. The project's own test corpus runs every program under Node and as a compiled binary and compares stdout, stderr and exit codes byte for byte, which is a better honesty signal than most 0.0.x releases offer.

Is scriptc production ready?

No. The README says experimental, the badge says Vercel Labs experiment, and the version is 0.0.16. Treat it as a candidate for one isolated, self-contained tool where startup is the binding constraint — never as a migration target for an existing service. Commenters quoted by InfoQ raised longevity as a concern, pointing at Vercel's zerolang, which stopped receiving commits weeks after launch; one attempt to compile the TypeScript 6 compiler with scriptc failed on an internal compiler error, though the same developer measured a 3.6ms cold start against Node's 48.9ms.

The practical next step is cheap and tells you almost everything: pick your smallest internal CLI, run scriptc coverage on it, and read the diagnostics. If coverage comes back fully static, you have a candidate worth timing. If it needs --dynamic for half the program, you have your answer, and the sensible move is to keep the build honest with the tooling you already run — type-aware linting on TypeScript projects moved much faster in 2026 without changing your runtime at all. For services rather than CLIs, the boring wins are usually elsewhere in the serverless architecture rather than in the language runtime, and our custom software development work tends to reach for a compiled language outright when startup is genuinely the constraint. Choosing between an incremental adoption and a wholesale rewrite has its own literature — the incremental versus big-bang rewrite question applies here too.

Frequently asked questions

Yes. scriptc compiles TypeScript to a native executable using the TypeScript compiler for parsing and type checking, then a typed IR, emitting C, LLVM IR, assembly, objects, native binaries or WebAssembly. The produced executables contain no Node and no JavaScript engine.

A benchmark reported by InfoQ measured median CLI startup of 1.78ms for scriptc 0.0.16, against 21.29ms for Bun 1.3.12 and 61.78ms for Node 24.18.0. A framework-free node:http server built with scriptc idled at 1.9MiB of memory.

No. scriptc is explicitly experimental, carries a Vercel Labs experiment badge and is at version 0.0.16 as of September 2026. It is reasonable to evaluate on one isolated CLI where startup dominates, and unreasonable as a migration target for an existing service.

Only at startup and on memory. One developer quoted by InfoQ measured compute roughly 7.5 times slower than Node 24, while the same binary started 12 times faster and used 72 times less memory. Long-lived, CPU-bound services gain nothing from that trade.

Only through the dynamic tier. Passing --dynamic embeds a quickjs-ng engine of roughly 620KB and runs that code inside it. In one benchmark Hono needed --dynamic, pushing 62% of the server into QuickJS and cutting throughput to 18.4k requests per second.

scriptc documents deliberate divergences: strings are stored as UTF-8, memory is reference counted rather than garbage collected, Object.keys reports declaration order, and process.argv[0] is scriptc. Reference counting is a semantic change that affects cyclic structures, not a tuning option.

Written by

Akash Mohapatra

Akash Mohapatra

Co Founder & Director

26 Sep 2026

·

6 min read

Share

LET'S CONNECT

Connect with Creuto!

Ready to take the first step towards unlocking opportunities, realizing goals, and embracing innovation? We're here and eager to connect.

We don't just aim to fit in – we strive to stand out. Experience the perfect blend of innovation, excellence, and trust that makes us truly unforgettable. Discover the difference with Creuto.

© 2026 Creuto All Rights Reserved