PerryTS and Zinc: what to learn

Research summary, 2026-09-28, from these sources:

PerryTS in one paragraph

PerryTS compiles TypeScript and JavaScript ahead of time to native code.

  • Pipeline: SWC → HIR → LLVM 22, a Rust workspace of about 90 crates.
  • Runtime: NaN-boxed values and a per-thread generational tracing GC. Types are erased (--type-check is optional).
  • Bet: npm compatibility, with about 95–97% of Node’s own test suite passing across 53 node:* modules.
  • UI: native platform widgets through a SwiftUI-like API.
  • Targets: 11 desktop, mobile, watch, TV and web targets; nothing embedded.
  • Size: about 330 KB for hello world, with about 3.2 ms startup.

Its strengths are presentation and process:

  • rustc-style diagnostics with codes and perry explain;
  • a check --fix command;
  • benchmarks published with the rows it loses, raw JSON artifacts and a staleness check;
  • doc examples compiled in CI;
  • one-line installs (npm, brew, winget, apt).

Zinc compared

PerryTSZinc
Typingerased, optional tscstrict TS checker, machine types (i32, fx12), gradual Dyn opt-in
Memorygenerational GCdeterministic refcount, TLSF, pools, arenas, @weak
ConcurrencyOS threads with deep-copied messagessingle-threaded async; the renderer is parallel in bands
UInative widgetsone pixel-identical renderer for every target (e-ink, LED matrices included)
Targetsdesktop, mobile, web+ ESP32, Pi 1, reMarkable, PS1/PS2
Size~330 KB, ~3.2 ms~70 KiB, ~2 ms
VerificationNode-parity test suitesbyte-exact sim oracle on every target, plus pixel goldens
Packagesnpmnone yet

Positioning: “TypeScript for devices: deterministic, tiny, verified”. Don’t compete with “your Node code just works”.

Adopted practices and their status

#PracticeStatus
1Editor setup matching the compiler: tsconfig with Zinc’s lib, zinc:* paths, JSX typings, tsserver plugindone: zinc init, zinc tsconfig, lib/editor/
2Honest benchmark headline: geometric mean of ratios, name the lossesdone: PERF.md, README, scripts/bench.mjs
3Diagnostic registry: code, title, why, fix, example; code frames; zinc explain Z1008; generated docs table; one fixture per codetodo
4zinc check --fix for mechanical rewrites (var→let, in→has…), with confidence tierstodo
5Benchmark rigor: 11 runs, p95 / σ, idle machine and clean tree, versioned JSON artifact, peers (XS, Espruino, MicroPython), compile timestodo
6Generated compatibility matrix (target × program / module / plugin) with a CI gatetodo
7Executable docs: compile every guide sample in CI, marker-checked numberstodo
8Distribution: npm package (npx zinc init), prebuilt SDK images, zinc doctor install hintstodo
9Incremental builds: one translation unit per module, cached runtime / plugins, published cold and hot timestodo
10Packages carrying a "zinc" field resolved from node_modulestodo
11End-of-build notice listing Dyn and unsupported sites (gradual profile)todo
12Debugging page: lldb / CodeLLDB with the emitted #linetodo
13Website: Getting started / Language / Limitations / Targets / Modules / CLI / Internals, compare pages, llms.txttodo

Edit this page on GitHub ↗