Perry Comprehensive Review: The V8-less Revolution from TypeScript to Native Machine Code
đŹđ§ No Node.js, no V8, no Electron! We look under the hood of the Perry compiler, which compiles your TypeScript code directly into native executables in seconds. Meet the NaN-boxing architecture, LLVM optimizations, and the 10-platform native UI revolution.
In the modern software development world, the dominance of JavaScript and TypeScript is indisputable. However, when we want to run these languages on the desktop or server, we pay a massive price: The V8 engine and runtime environments like Node.js/Deno/Bun.
It is no secret that these architectures are âclunkyâ, requiring tens of megabytes of memory just to print a simple âHello Worldâ, or embedding the entire Chromium engine into our project when we build desktop applications with tools like Electron or Tauri.
But what if I told you there is a compiler that can translate your TypeScript code directly into machine code (native binary) understood by the operating system, just like C, C++, or Rust, with no runtime dependencies, producing executable files as small as ~330 KB?
Meet Perry!, the native compiler that breaks the âspeed and sizeâ chains of modern web developers, backed by the incredible power of Rust and LLVM, compiling TypeScript to machine code.
In this comprehensive technical review, we wonât just give a superficial introduction to Perry; we will dive deep into the codebase and witness line-by-line how NaN-boxing architecture, thread-local Arena allocation, and LLVM optimizations work under the hood. Just as Gea.js buried the Virtual DOM, Perry is preparing to bury the V8 engine in desktop and server applications.
1. Beyond the Era of V8 and Node.js: What is Perry? đ€
TypeScript is essentially a language that transpiles to JavaScript. In the traditional flow, tsc or esbuild converts your code to JavaScript, and then a JIT (Just-In-Time) compiler like V8 or JavaScriptCore converts this code into machine code at runtime.
The core philosophy advocated by Perry is this: âWhy run a massive compiler (V8) on the userâs computer to convert code to machine language? Why not do this on the developerâs computer (Ahead-Of-Time)?â
Perry is not an interpreter or a virtual machine. It is a pure, real, and ruthless compiler. It takes your TypeScript code and transforms it directly into executable binary files for Windows (.exe), macOS, Linux, and even iOS/Android.
The core advantages this gives you are:
- Zero Dependency: There is no need for Node.js, JRE, or .NET to be installed on the userâs system. It produces a single executable, just like Go or Rust.
- Instant Startup: There is no JIT âwarmupâ time. Your application starts in milliseconds.
- Small Size: While classic Electron applications start at 150 MB, Perryâs Hello World applications featuring native UI are hundreds of kilobytes in size.
- Resource Consumption: Because there is no massive Garbage Collector and optimization threads running in the background by V8, RAM usage is exceptionally low.
2. Under the Hood: The Rust, SWC, and LLVM Triangle đ«
Perryâs power comes from being built on the most solid foundation of the modern software ecosystem. The compiler itself is written entirely in Rust.
The Compilation Pipeline consists of three main stages:
- Parsing - SWC: Perry uses the blazingly fast Rust-based SWC library, which Next.js also uses, to parse your TypeScript code and create an Abstract Syntax Tree (AST).
- HIR (High-Level Intermediate Representation): The AST from SWC is converted into Perryâs internal architecture, HIR (
perry-hir). At this stage, variable scopes, closure captures are calculated, and type inference is performed. - LLVM Codegen: The biggest magic starts here. In Perryâs early versions,
Craneliftwas used for fast compilation. However, Cranelift was a JIT compiler focused on âfast code generationâ. To break the optimizer ceiling of the application and provide full support for architectures like Apple Watch (arm64_32), the Perry team undertook a massive architectural migration and fully transitioned to the LLVM infrastructure. LLVM is the very infrastructure that compiles Appleâs Swift, C++ (Clang), and Rust. Thus, Perry gets all of the 30-year âindustry standardâ code optimization techniques (Loop vectorization, LICM, GVN, etc.) for free!
3. The Secret of Data Representation: âNaN-Boxingâ Magic đȘ
JavaScript is a dynamically typed language. A variable can be a number one moment and an object the next. In traditional C/C++ structures, you have to keep variables in large structs and on the Heap to manage this. But this is very slow.
Perry uses a brilliant trick that V8 and JavaScriptCore also use: NaN-Boxing. The files crates/perry-runtime/src/value.rs and crates/perry-codegen/src/nanbox.rs that we reviewed form the heart of this architecture.
How Does NaN-Boxing Work?
According to the IEEE 754 standard, the space reserved for 64-bit double floats is so huge that there are trillions of invalid combinations marked as âNaNâ (Not a Number).
Perry uses the 64-bit register as follows: If the bits are a valid number, they are processed directly on the CPU. If not, the top 16 bits (Tag bits) indicate the âtypeâ of the data (Undefined, Null, Boolean, String, Object). The remaining 48 bits (Payload) hold the value of the data or its pointer on the Heap.
What does this mean? When you do simple integer arithmetic, no object is created, and no memory allocation is made. Everything happens directly in the CPUâs fastest hardware registers.
4. Memory Management: The Arena Allocator and GC Challenging V8 đïž
JavaScript means millions of objects constantly created and destroyed. Because Perry doesnât use a massive memory engine like Node.js, it houses its own wonderful memory manager inside perry-runtime.
Thread-Local Bump Allocator and mimalloc (Zero-Cost Allocation)
What happens when you create a new object (new ClassName())? The crates/perry-runtime/src/arena.rs file gives us the secret: While Perry uses the legendary mimalloc as the global memory manager, it allocates a special memory pool (Arena) for each operating system thread. When a new object is needed, complex OS calls (malloc) are not made. The pointer is simply shifted forward a few bytes (Bump Allocation), and this operation is coded âinlineâ directly at the LLVM IR level. This is literally a âzero-costâ operation at the hardware level and is as fast as stack allocation in C.
Conservative Mark-Sweep GC
So how is the filled memory cleaned up? The Mark-Sweep Garbage Collector implemented in crates/perry-runtime/src/gc.rs performs âconservative stack scanningâ. That is, it reads the CPUâs stack, finds values that could be pointers, marks the objects connected to them as âAliveâ (Mark), and wipes away everything else (Sweep). Moreover, thanks to the Adaptive Malloc-Trigger logic, the GC smartly determines when the next cleanup should be done based on the efficiency of the last deletion. This minimizes CPU pause times.
5. Advanced Compiler Optimizations (Performance Secrets) đ
We can see in detail why Perry is so fast in the PERF_ROADMAP.md documents. The compiler doesnât just translate code into machine language; it also makes the code âsmarterâ:
- Escape Analysis and Scalar Replacement: If you use an object (or array) only within a function and finish its job, Perry doesnât create that object on the Heap! It breaks down the object and places its properties directly into CPU registers (Scalar Replacement). Since the object is never created, the Garbage Collector doesnât get tired.
- i32 Fast Path and Fast-Math Flags: If your code has mathematical operations (or loop counters), Perry translates this to native 32-bit integer (i32) math instead of JavaScriptâs standard
double floatcalculation. Thanks to thereassoc contract(fast-math) flags passed to LLVM, it runs a full 24.6 times (24.6x) faster than Node.js in heavy mathematical operations likefactorial(24ms vs Nodeâs 591ms)! - Monomorphic Inline Cache (PIC): The Inline Cache structure, which is the heart of modern JS engines, exists natively in Perry. When you repeatedly access a property of an object of the same type (PropertyGet), Perry caches the memory offset directly instead of dynamically searching for the method and loads it straight from memory.
- Typed Buffer Locals (noalias metadata): Normally, when reading data structures like
BufferorUint8Array, a âNaN-Box unboxâ must be performed at each step. Perry statically determines buffer types, tags them with LLVMâsnoaliasmetadata, and emits pointer loads directly. In image processing (e.g., 4K 5x5 blur) tests, this optimization allows it to achieve exactly the same performance (zero overhead) as Zig or C. - SIMD Vectorization: Independent mathematical operations within loops (Loop-Invariant Code Motion) and arrays are automatically converted into machine instructions that can process multiple data points simultaneously (SIMD - Single Instruction, Multiple Data) by LLVMâs smart analyses.
Future Performance Goals: Catching Up to Zig
Currently, although Perry overwhelmingly beats Node.js in almost all tests (up to 24.6x in the factorial test), some optimizations are expected to be completed to fully reach pure C/Zig speeds. For example, in the image_conv (5x5 pixel blur process at 4K resolution) benchmark, Perry currently runs around 457ms, while Zig is at the top with 246ms. To close this architectural gap, âTyped Buffer Localsâ (using pure i64 slots to skip unboxing in buffer accesses), âInterior/border loop splittingâ (removing unnecessary bounds checks that block vectorization), and âDouble-ABI FNV-1a hashâ optimizations are the focus of the next release (PERF_ROADMAP).
6. Throw Chromium in the Trash: 100% Native UI with SwiftUI Elegance đ±
When you wanted to bring your Node.js application to the desktop, Electron was your only choice, and your applicationâs size instantly ballooned to 150 MB, with RAM usage skyrocketing to 300 MB.
Perry offers a revolutionary approach: Perry UI (@perry/ui). Its syntax is incredibly similar to Appleâs SwiftUI architecture: you write interfaces with TypeScript using declarative components like VStack, HStack, ZStack, State, and Button.
1
2
3
4
5
6
7
8
9
10
import { App, Text, Button, VStack, State } from "perry/ui";
function Counter() {
const count = State(0);
return VStack(16, [
Text(`Counter: ${count.value}`).font("title"),
Button("Increment", () => count.value++)
]);
}
However, this interface is not rendered to the DOM using HTML/CSS or WebView in the background! Perryâs genius lies here: these components you write are directly mapped to the operating systemâs 25+ real native widgets (buttons, text fields, tables, canvas, scrollable areas, QR codes, splash screens, etc.):
- On macOS:
VStacktransforms directly intoNSStackView, buttons are realAppKit/Cocoabuttons. - On Windows: Real
Win32 / WinUIcomponents. - On Linux:
VStacktranslates directly toGtkBox, buttons are nativeGTK4elements. - iOS/Android:
UIStackViewand native platform elements.
This way, with just a 5-10 MB .exe or .app file, you can write desktop programs that are 100% compatible with the operating system, run smoothly at 60 FPS, and have almost âzeroâ RAM consumption.
7. Real OS Threads: parallelMap and spawn đ§”
Multiprocessing in the Node.js world is a nightmare; you have to spin up Web Workers, copy data with postMessage, and pay the serialization cost.
In Perry, you have access to the operating systemâs Real OS Threads. Moreover, you do this with a single line of code:
1
2
3
4
import { parallelMap } from "perry/thread";
// Distributes the data across all cores of the operating system!
const results = parallelMap(data, (item) => heavyComputation(item));
The perry/thread package manages C/C++ threads in the background. Closures are copied to memory (with deep-copy logic), and mutation errors are prevented at compile-time. The parallel processing architecture (parallelFilter, spawn) that data scientists or image processing engines dream of is at the center of Perry.
8. A World First: Built-in UI Testing with Geisterhand (Ghost Hand) đ»
End-to-End (E2E) testing desktop applications is painful. Perry radically solves this problem with an incredible plugin called âGeisterhandâ (German for Ghost Hand).
If you compile your project with the --enable-geisterhand flag:
1
perry app.ts -o app --enable-geisterhand
Perry embeds a hidden HTTP Server (default: port 7676) inside your executable file. While your application is running, you can send HTTP requests from the outside to click any button in your program, write text into TextFields, drag sliders, or take screenshots. Clunky testing tools like Selenium or Puppeteer are no longer needed. With Python, cURL, or any REST tool, you can perform full Chaos Fuzzing or UI automation on your native desktop application!
9. The Mind-Blowing Feature: NPM Packages Turning into Native Rust Crates đŠ
For a compiler to rival Node.js, it must support the NPM ecosystem. If you are asking, âWill the NPM packages I wrote work?â, Perry literally puts on a show here.
According to the projectâs native-libraries.md documentation, Perry maps the most popular 30+ NPM packages to native Rust crates! When you import an NPM package normally in your TypeScript code, Perry compiles it with Compile-Time Plugin logic; meaning instead of running the Node.js module in the background, it embeds its high-performance Rust equivalent directly into the machine code (Zero runtime plugin overhead, zero IPC boundaries):
- Database: Popular drivers like
mysql2,pg,mongodb,better-sqlite3,ioredisuse C/Rust systems in the background. - Security:
bcrypt,argon2,jsonwebtoken, andcryptooperations are processed instantly at native speed. - HTTP/Network: When you use
http,https,axios,node-fetch,ws, andnodemailer, Rustâs famousreqwestandtungstenitecrates kick in. - Data & Utils: Dozens of packages like
cheerio(Rustscraper),sharp,zlib,lodash,dayjs,uuid,dotenv,validatorrun completely native.
Additionally, Perry natively hosts core Node.js APIs from scratch such as fs, path, crypto, os, and child_process through perry-stdlib. All this translates to a ~0 ms startup time the moment your application launches.
For Those Who Canât Give Up V8: Optional V8 Engine Support
If you strictly need to use a specific NPM package in your application that only runs with pure JavaScript and doesnât have a native equivalent yet, Perry still wonât let you down. If you add the --enable-js-runtime flag at compile time, Perry embeds a hidden V8 engine into your application. In this case, your binary size jumps from the standard 2-5 MB levels to 15-20 MB levels, but you instantly gain the massive compatibility of the pure JavaScript NPM ecosystem.
Real-World Test: Hono, tRPC, and Strapi đ
What weâve explained is not theoretical. As announced on Perryâs official blogs; massive frameworks like Hono, tRPC, and Strapi can currently be compiled smoothly with Perry. Perry takes hundreds of modules that make up these frameworks, links them together, and inlines them into a single native ARM64 executable file under 2 MB in under 1 second. Architectures that create massive runtime overhead in Node.js turn into a âzero-costâ composition in Perryâs machine code.
Real-World Applications Built with Perry (Showcase)
Perry has proven itself not only in âhello worldâ tests but also in complex real-world projects. Here are some tremendous products featured on the homepage and built end-to-end using TypeScript with Perry:
- Mango: A native MongoDB database management tool that opens instantly (under 1 second). It has only a ~7 MB binary size and consumes less than 100 MB of RAM (macOS, Windows, Linux, iOS, Android).
- Hone: A native, AI-first code editor. Features a built-in terminal, Git integration, and LSP (Language Server Protocol) support. Offers sub-second startup, <50 MB binary size, and <100 MB memory consumption (All platforms and Web).
- dB Meter: A native decibel/sound level measurement app running with 60fps updates and live soundwave animations (iOS, macOS, Android).
- Pry: A native JSON viewer developed as a reference by the Perry team. Offers tree navigation and fast search directly via the operating systemâs native interface without WebView or Electron.
- Perry Demo: An interactive test platform where you can make live speed and size comparisons between Perry, Node.js, and Bun.
- Perry Starter: A minimal project template prepared to quickly start Perry projects.
So Whatâs Missing? (Conscious Constraints)
Perry is not a magician; itâs a compiler with rules. Due to the nature of AOT (Ahead-Of-Time) compilation, some features that require interpreting code at runtime or dynamically changing the runtime memory tree are consciously not supported (typescript-parity-gaps.md):
eval()ornew Function()are not supported.ProxyandReflectAPIs are deliberately omitted because they would impose a massive âtrapâ cost on every property access in the CPU.- Flat memory layouts are preferred over dynamic
import()calls and complex prototype chains. - Dynamic property assignment on
this: Computed function assignments at runtime likethis[methodName] = function()are not yet fully supported (e.g., dynamically injecting route registrations in Hono). These âlimitationsâ are actually the primary reasons Perry can run at the same speed as C++ and Zig.
10. A Growing Ecosystem: Databases, Push Notifications, and React đ
Perry is growing from just a compiler into a full-fledged ecosystem. The latest developments in the projectâs source codes include:
perry-prisma,perry-sqlite,perry-postgres: Native database drivers that are 100% compatible with the Prisma ORM API, but use Rustâs high-performancesqlxlibrary under the hood instead of Node.js, operating at zero cost at the FFI boundary.perry-push(Universal Push Notifications): The ability to send push notifications natively to all APNs (iOS/macOS), FCM (Android), Web Push, and WNS (Windows) platforms through a single library. For cryptography, Rustâs minimalringcrate is used instead of massive OpenSSL packages.perry-verify: An automated built-in testing system (App Verification) that spins up the compiled application. It tests your app, triggers UI events in a cross-platform manner, and performs visual validation via Claude AI integration in non-deterministic scenarios.perry-react: A revolutionary compatibility layer for React developers that supports JSX and Hooks likeuseState, but instead of creating a Virtual DOM in the background, it binds components directly to native operating system interfaces (AppKit, UIKit, Win32, etc.).
11. Limitless Platform Freedom: 10 Different Targets đ
We talked about Native UI capabilities on the desktop. But we see that Perryâs vision is not limited to the desktop alone. You can port your Perry codes to:
- macOS (x86_64, aarch64)
- Windows
- Linux (Ubuntu/GTK4)
- iOS and iPadOS
- Android
- watchOS and tvOS
- Web (WebAssembly) and Web/JavaScript
target platforms with a single command line via cross-compilation. You can even compile your Windows, macOS, and iOS applications directly from a single Linux machine (for example, on an Ubuntu CI/CD server) without needing a Mac device (thanks to lld-link and ld64.lld support).
12. Long Story Short: How Do We Start?
To try out this whole âV8-less native TypeScriptâ world, you donât need to install anything on your system. The development experience is incredibly streamlined. Just open your terminal and type this (you can compile to 9 different platforms):
1
2
npx perry build src/main.ts -o app
./app
Thatâs it! Your app file is now machine code that acts just like a C/C++ program, needing neither Node.js nor any other dependencies.
In conclusion, Perry is a tremendous engineering marvel that takes the flexibility and ease of the JavaScript world and blends it with Rustâs system programming power and LLVMâs hardware optimizations. If you want to leave behind the âlazinessâ of Electron and heavy JavaScript runtimes and write true native applications that open in a thousandth of a second, Perry is the future itself.
To inspect the project and the source codes under the hood without wasting any more time, you can check out the Perry GitHub Repository! đ
