Verdict: Yes, you can compile TypeScript directly to native machine code in 2026 — no Node.js, no V8, no JavaScript engine in the binary. Two open-source compilers make this possible today: scriptc (from Vercel Labs) and Perry (from the PerryTS project). Both use LLVM to emit real machine code, but they make radically different trade-offs. For most developers evaluating this right now, scriptc produces the smallest binaries (~170KB) and Node-like fidelity, while Perry offers deeper AOT optimization with cross-compilation to 10 targets. Neither is production-hardened yet — both are pre-1.0 — but for CLI tools, serverless functions, and edge deployment, compiling TypeScript to native is no longer hypothetical.
Last verified: 2026-07-30
- ScriptC (Vercel Labs): TypeScript → native, ~170-200KB binaries, ~2.4ms startup. Apache 2.0, GitHub.
- Perry (PerryTS): Rust-based TS compiler, ~330KB-5MB binaries, ~1ms startup. MIT, GitHub.
- Node SEA: Embeds the full Node binary, ~48-118MB. Stable since Node 22.
- Bun --compile: Embeds the Bun runtime, ~60-100MB. Stable.
- Both scriptc and Perry are pre-1.0 — verify against your own workloads before production use.
How does compiling TypeScript to machine code actually work?
TypeScript-to-native compilation means your .ts source code is parsed, type-checked, lowered to an intermediate representation, and then emitted as real machine code — the same pipeline that languages like Rust, C++, and Go use. No JavaScript runtime is included in the resulting binary. No JIT. No V8 or JavaScriptCore engine boots up when you run it. The binary starts, executes native instructions, and exits.
This is fundamentally different from what most developers call "compiling TypeScript." When you run tsc or esbuild, you are transpiling — converting TypeScript syntax to JavaScript. The output still requires Node.js, Bun, or a browser to execute. Even Node.js Single Executable Applications (SEA) and bun build --compile only embed your JavaScript inside a copy of the runtime engine. The JS is still parsed and JIT-compiled at startup.
AOT (ahead-of-time) compilation skips all of that. The machine code is generated once, at build time, and runs directly on the CPU with no intermediary.
What are the two TypeScript-to-native compilers available in 2026?
ScriptC (Vercel Labs)
ScriptC is an open-source TypeScript-to-native compiler from Vercel Labs, available on GitHub under the Apache 2.0 license. As of late July 2026, it has accumulated roughly 2,000 stars.
How it works: ScriptC uses the real TypeScript compiler (tsc) for parsing and type-checking. Your code is lowered to a typed intermediate representation, then to C, and finally compiled via clang/LLVM to native machine code. No changes to your TypeScript code are required — no new syntax, no annotations, no dialect. The same .ts files you run on Node are type-checked and compiled to native.
Three tiers of compilation:
- Static compilation (default): Code that can be fully statically analyzed is compiled directly to native machine code. No JavaScript engine is included in the binary.
- Dynamic fallback (
--dynamicflag): For npm dependencies andany-typed code that cannot be statically compiled, scriptc embeds quickjs-ng (~620KB) — a compact JavaScript engine — to execute those portions at runtime. Every value crossing back into static code is validated with runtime type checks. - Rejected: Unsupported constructs fail with specific error codes and rewrite hints rather than silently producing incorrect output.
Key metrics (per the project's own benchmarks on Apple M-series):
| Metric | scriptc (static) | scriptc (--dynamic) | Node.js | Node SEA |
|---|---|---|---|---|
| Binary size | 170–200 KB | ~3 MB | — | 60–100 MB |
| Startup time | ~2.4 ms | ~2.4 ms | ~47 ms | ~47 ms |
| Memory (RSS) | 1–4 MB | 1–4 MB | 67–116 MB | 67–116 MB |
Source: scriptc GitHub README (verified 2026-07-30)
Node API coverage: ScriptC supports fs, path, net, http, https, tls, crypto, fetch, child_process, os, url, zlib, typed arrays, Buffer, process, timers, and signal handlers. The server stack (net, http, https, tls) is vendored with its own mbedTLS implementation — no system HTTP dependency.
Correction model: ScriptC runs differential testing — 800+ test programs are executed under both Node and scriptc, and stdout/stderr/exit codes must match byte-for-byte. AddressSanitizer with reference-count auditing catches memory errors as build failures. (Source: scriptc GitHub)
Perry (PerryTS)
Perry is a native TypeScript compiler written in Rust, available on GitHub under the MIT license. Created in January 2026, it has accumulated over 4,300 stars.
How it works: Perry uses SWC — the Rust-based TypeScript parser — to parse source files into an AST. The AST is lowered to a High-Level Intermediate Representation (HIR), type information is resolved, generics are monomorphized, and LLVM IR is emitted. LLVM handles optimization passes (inlining, constant propagation, loop vectorization, dead-code elimination) and machine-code generation for the target platform.
Key metrics (per Perry's benchmarks on an M1 Max, Perry v0.5.279 vs Node.js v25):
| Metric | Perry | Node.js | Bun |
|---|---|---|---|
| Binary size (hello world) | ~330 KB | ~80 MB | ~90 MB |
| Binary size (typical CLI) | 2–5 MB | — | — |
| Startup time | ~1 ms | ~30 ms | ~10 ms |
| Cross-compilation targets | 10 (incl. iOS, Android) | No | Yes |
Source: Perry — Compile TypeScript to a Binary (verified 2026-07-30)
Benchmark performance (Perry v0.5.279 vs Node.js v25, M1 Max, 11-run median):
| Benchmark | Speedup (vs Node) | Perry | Node.js |
|---|---|---|---|
| accumulate | 18x | 34 ms | 617 ms |
| object create | 11x | 1 ms | 11 ms |
| JSON roundtrip | 5.3x | 75 ms | 394 ms |
| fibonacci | 3.2x | 318 ms | 1022 ms |
| array read | 3.3x | 4 ms | 13 ms |
Source: perryts.com performance comparison (verified 2026-07-30)
JavaScript conformance: Perry's test262 conformance sits at String 79%, Array 72% as of v0.5.1146. This is measured and published, not inherited from V8. Growing npm package support includes axios, zod v4, express, fastify, and hono — but full npm compatibility is not yet available. An optional V8 fallback exists for packages that cannot compile natively.
ScriptC vs Perry: which should you use?
| Dimension | scriptc (Vercel Labs) | Perry (PerryTS) |
|---|---|---|
| Language | TypeScript (Node/JS ecosystem) | Rust |
| Parser | Real tsc |
SWC |
| Code generation | C → clang/LLVM | LLVM IR (direct) |
| Binary size (hello world) | ~178 KB | ~330 KB |
| Binary size (with deps) | ~3 MB (--dynamic) | ~5 MB (typical CLI) |
| Startup time | ~2.4 ms | ~1 ms |
| JS engine fallback | quickjs-ng (~620KB) | Optional V8 fallback |
| Cross-compilation | macOS arm64 (primary), cross-compile for Linux/Windows | 10 targets including iOS, Android |
| npm compatibility | Via --dynamic (embeds QuickJS) | Growing (axios, zod, express, fastify, hono) |
| Differential testing | Yes (800+ tests, byte-for-byte) | Published benchmarks |
| License | Apache 2.0 | MIT |
| GitHub stars | ~2,000 | ~4,300 |
| Created | July 2026 | January 2026 |
| Stability | Pre-1.0 (v0.0.17 at launch) | Pre-1.0 |
The honest verdict: Both tools are pre-1.0 and neither has independent third-party benchmarks yet — every number comes from the project's own test harness. For CLI tools and lightweight scripts where binary size matters most, scriptc's static path delivers the smallest binaries (~178KB) with byte-exact Node fidelity. For cross-platform deployment (including mobile targets) and deeper AOT optimization, Perry's broader LLVM target support is the advantage. Both are actively evolving — pin a version and test against your actual workloads.
How do these compare to Node SEA and Bun --compile?
Node SEA and Bun --compile are not AOT compilers. They package your JavaScript inside a copy of the runtime engine. The binary runs the full V8 (Node) or JavaScriptCore (Bun) JIT at startup — parsing, interpreting, and hot-compiling your code just like always. The only difference is that you don't need a separate Node.js or Bun install on the target machine.
| Approach | What ships | Binary size | Startup | Execution model |
|---|---|---|---|---|
| scriptc (static) | Native machine code only | 170–200 KB | ~2.4 ms | AOT, no engine |
| Perry | Native machine code only | ~330 KB – 5 MB | ~1 ms | AOT, no engine |
| Node SEA | Node binary + bundled JS | 48–118 MB | ~30–47 ms | JIT (V8) |
| Bun --compile | Bun runtime + bundled JS | 60–100 MB | ~10 ms | JIT (JavaScriptCore) |
Node SEA has been stable since Node.js 22 (April 2024) and received improvements in Node 24. The Node binary itself is ~48 MB on Linux x64, ~46 MB on macOS arm64, and ~50 MB on Windows x64, with your application code adding 1–10 MB on top.
Bun --compile is stable and produces smaller binaries than Node SEA (~60 MB macOS arm64, ~100 MB Linux x64), but still ships the entire Bun runtime embedded. (Source: perryts.com comparison, verified 2026-07-30)
The practical takeaway: if your users need the full npm ecosystem with zero compatibility risk, Node SEA or Bun --compile are the safer choices. If binary size, startup speed, or memory footprint are the bottleneck — CLI tools, serverless functions, edge deployment — AOT compilation through scriptc or Perry is the approach that actually eliminates the runtime.
What can you actually build with compiled TypeScript?
Real-world use cases where compiled TypeScript wins:
CLI tools: A CLI written in TypeScript that ships as a 200KB binary with ~2ms startup instead of requiring users to install Node.js. Your users get instant execution; you don't ship 80MB of runtime for a
file-convertutility.Serverless functions: Cold-start latency is the primary UX bottleneck in serverless. A native binary boots in 1-2ms instead of 30-50ms of runtime initialization. On platforms like AWS Lambda or Cloudflare Workers, this affects every request that hits a cold instance.
Edge and embedded: Devices with limited memory and storage benefit from 1-4MB RSS instead of 67-116MB. Perry's cross-compilation to ARM targets makes TypeScript viable on platforms where Node.js was never practical.
Desktop apps: Instead of shipping a full browser engine (the Electron model), a native TypeScript binary can use AppKit, UIKit, or GTK directly — Perry lists native UI support for these frameworks.
Distributable CLIs in an AI-powered workflow: If you're building tools that other AI agents use — like the CLI-first testing tools that integrate into agent loops — a self-contained binary that doesn't depend on the target machine having Node installed removes an entire class of setup friction.
How to compile TypeScript to a native binary with scriptc
Step-by-step, from install to running binary:
# 1. Install scriptc globally
npm install -g scriptc
# 2. Write your TypeScript (standard .ts, no modifications)
# Example: fib.ts
# function fib(n: number): number {
# return n < 2 ? n : fib(n - 1) + fib(n - 2);
# }
# console.log(fib(30));
# 3. Compile to native binary
scriptc build fib.ts
# 4. Run with no Node.js required on this machine
./fib
# Output: 832040
# 5. Check the binary size
ls -la fib
# -rwxr-xr-x 178K fib
To check how much of your codebase can compile statically before building:
scriptc coverage app.ts
This reports the percentage of statements that compile to native code and lists any blockers with specific error codes and rewrite hints.
For npm dependencies:
# Add --dynamic to embed QuickJS for npm deps
scriptc build app.ts --dynamic
How to compile TypeScript to a native binary with Perry
# 1. Install Perry (Homebrew on macOS, APT on Linux, winget on Windows)
brew install perry
# 2. Compile TypeScript to a binary
perry compile main.ts
# 3. Run with no runtime required
./main
# 4. Type-check first (optional)
perry check main.ts
Perry cross-compiles to 10 targets from a single machine, including Windows, macOS, iOS, and Android from a Linux build host.
What are the limitations and risks of native TypeScript compilation?
Both scriptc and Perry are pre-1.0 projects. Here is what you need to know before depending on either:
ScriptC limitations:
- The static compilation path covers most TypeScript language features and Node's standard library, but npm dependencies require the
--dynamicflag (which embeds QuickJS and grows the binary to ~3MB). - The project was created in July 2026 with a small contributor count. Hacker News discussion flagged the possibility of AI-generated code, with one expert describing the architecture as "idiotic" — specifically criticizing the use of float-based number representation instead of native integers. (Source: explainx.ai analysis, verified 2026-07-30)
- No independent third-party benchmark exists yet. All performance numbers come from the project's own test harness.
Perry limitations:
- JavaScript conformance is measured and published (test262: ~79% String, ~72% Array as of v0.5.1146) but not yet complete. This means some JavaScript behaviors may not produce identical output to Node.js or Bun.
- npm package support is growing but not comprehensive. Packages like axios, zod v4, express, fastify, and hono compile natively; others may require the optional V8 fallback.
- Perry's documentation mentions telemetry in the CLI that connects to perryts.com during compilation. If you operate in a restricted network environment, be aware of this behavior.
- As with scriptc, all benchmarks are self-reported from Perry's own test harness.
Shared risks:
- Both tools are evolving rapidly — APIs, CLI flags, and supported features may change between versions.
- Neither is a drop-in replacement for Node.js. If your application relies on the full npm ecosystem, Node SEA or Bun --compile remain the pragmatic choice.
- AI-generated code in your own projects already raises verification challenges; adopting a pre-1.0 compiler built during the same AI coding era compounds that risk. Pin versions and run your own integration tests.
What this means for you
If you build CLI tools, serverless functions, or distributable binaries in TypeScript: these compilers eliminate your largest deployment headache — the runtime. Start evaluating scriptc for static TypeScript with minimal dependencies, or Perry for cross-platform targets including mobile. Pin a version, run your actual code through the compiler, and verify output byte-for-byte against Node.js.
If you build full-stack applications with heavy npm dependencies: stick with Node SEA or Bun --compile for now. The npm ecosystem is not yet fully compatible with either AOT compiler, and the runtime embedding cost is manageable for server-side deployment where disk space is cheap.
If you're building tools for AI agent workflows: a self-contained binary that agents can download, execute, and clean up — without managing Node.js installs — removes a real friction point. This is the sweet spot for native TypeScript compilation in 2026.
FAQ
Q: Can TypeScript be compiled to machine code? A: Yes. In 2026, two open-source compilers — scriptc (Vercel Labs) and Perry (PerryTS) — compile TypeScript directly to native machine code using LLVM. The output is a standalone binary with no JavaScript engine, no Node.js, and no V8 runtime.
Q: What is the smallest TypeScript native binary you can produce? A: ScriptC produces ~170-200KB binaries for static TypeScript (no npm deps). Perry's hello-world binary is ~330KB. Both are orders of magnitude smaller than Node SEA (48-118MB) or Bun --compile (60-100MB).
Q: Can I use npm packages with native TypeScript compilation?
A: Partially. ScriptC supports npm packages via its --dynamic flag, which embeds the QuickJS-ng engine (~620KB) to run dependencies and any-typed code at runtime. Perry has growing native support for packages like axios, zod, and express, with an optional V8 fallback for the rest. Neither offers full npm compatibility yet.
Q: Is compiling TypeScript to native faster than Node.js? A: Startup is significantly faster — scriptc starts in ~2.4ms vs Node's ~47ms, and Perry starts in ~1ms. Runtime performance on compute-heavy benchmarks is also faster (Perry reports 3-18x speedups on specific benchmarks vs Node v25). However, all benchmarks are self-reported and pre-1.0; verify against your own workloads.
Q: Should I replace Node.js with a native TypeScript compiler? A: Not for most production applications. Both compilers are pre-1.0 with incomplete npm ecosystem support. Use them for CLI tools, serverless functions, and edge deployment where binary size and startup time are the priority. For full-stack apps relying on the npm ecosystem, Node SEA or Bun --compile remain the pragmatic choice.
Q: Does compiled TypeScript work on all platforms? A: Perry supports 10 cross-compilation targets including macOS, Linux, Windows, iOS, and Android. ScriptC's primary platform is macOS arm64, with cross-compilation to Linux and Windows. Neither produces architecture-independent output — you compile for a specific target, unlike Node.js which runs anywhere Node is installed.

Discussion
0 comments