# Tov: TypeScript-like language that compiles to native binaries

> Source: <https://tov.sh>
> Published: 2026-10-10 15:50:15+00:00

A programming language for coding agents. Your agent already writes TypeScript, so it can already write Tov, and when it gets something wrong the compiler hands back the edit that fixes it. What it ships is a native binary that beats Rust's.

binary_trees: millions of small objects made and freed

Tov260 msfastest

Bun376 ms45% slower

Node449 ms1.7× slower

C688 ms2.6× slower

Rust791 ms3.0× slower

Compiling a program to a native binary

Tov42 msfastest

C (clang)49 ms17% slower

Rust (rustc)98 ms2.3× slower

Memory, serving that JSON API

Tov1.6 MBleast

Rust (axum)4.4 MB2.8× more

Bun17 MB10.4× more

What happens when Tov builds a program

Tov is a compiler. It checks the TypeScript your agent wrote, writes it out as C and hands that to clang, so what you ship is one native binary with nothing to install next to it.

1TypeScriptshapes.tov

2Check0.3 ms

3C0.2 ms

4Binary55 ms · 34 KB

Your agent writes TypeScript

Tov reads TypeScript's syntax and types, with int and f64 for numbers, and Bun's and Node's APIs. It leaves out what makes a program hard to follow: any, null and ==.

```
type Shape =
  | { kind: "circle", r: f64 }
  | { kind: "rect", w: f64, h: f64 }

function area(s: Shape): f64 {
  switch (s.kind) {
    case "circle":
      return Math.PI * s.r * s.r
    case "rect":
      return s.w * s.h
  }
}
```

Tov checks every line

The checker, written in Rust, catches what TypeScript lets through: a call that can fail without try, a value shared where it should be copied, a switch that misses a case. Each error has a code and, where the fix is clear, the edit that makes it, which tov check --json hands to your agent.

``` bash
// without the "rect" case
$ tov check shapes.tov
error[F0301] shapes.tov:6:11: `switch` is not
  exhaustive: missing "rect"
    6 |   switch (s.kind) {
  fix: add case "rect"
```

It writes the program as C

The whole program becomes one C file. Records become structs and a union becomes a tagged union, so switch (s.kind) is a switch on an integer. Values are reference-counted, with the counting written into the C, so there's no garbage collector to pause the program.

```
struct Shape {
    uint32_t tag;
    union { Circle m0; Rect m1; } u;
};

static double area(Shape s) {
    switch (s.tag) {
    case 0: return 3.141592653589793 * s.u.m0.r * s.u.m0.r;
    case 1: return s.u.m1.w * s.u.m1.h;
    }
}
```

clang makes one binary

clang compiles that C with Tov's runtime, a C library with the event loop, the HTTP server and client, and Tov's strings, arrays and maps. The runtime is compiled once and cached, so most of a build is clang's. A program that imports an npm package also carries the package and JavaScriptCore, the engine Bun uses, to run it.

``` bash
$ tov build shapes.tov --time
check 0.3 ms, generate 0.2 ms, C compile 55 ms
build: shapes
$ ls -lh shapes
34K shapes
$ ./shapes
circle: 3.14
rect: 6.00
```

How an agent writes Tov

1Your agent writes TypeScript

``` js
// hello.tov
Bun.serve({
  port: 3000,
  fetch(req) {
    const url = new URL(req.url)
    return new Response(`Hello from ${url.pathname}`)
  },
})
```

2Tov hands back the fix

``` bash
$ tov check --json
{
  "code": "T0831",
  "message": "`new URL` can throw `TypeError`; mark the call `try` to pass the error on, or catch it",
  "fixes": [{
    "applicability": "safe",
    "edits": [{ "line": 4, "col": 17, "text": "try " }]
  }]
}
```

3You ship a native binary

``` js
// the agent applies the fix
const url = try new URL(req.url)

$ tov build hello.tov
build: hello
$ ls -lh hello
152K hello
$ ./hello & curl :3000/agents
Hello from /agents
```

It's TypeScript

With the APIs your agent knows from Bun and Node. Bun.serve, fetch, node:fs, async/await and npm packages work as it expects.

Less to get wrong

There's no any, null or ==, and a call that can fail is marked try. If code looks like TypeScript it behaves like TypeScript, and if it can't, it doesn't compile.

A 42 ms compile

--watch restarts the program on save, and tov test runs the tests that sit next to the code.

Against Rust, C, Bun and Node

Tov compiles to C and counts references instead of collecting garbage, so there are no collector pauses and the binaries stay small.

Programs

binary_trees3.0× faster than Rust

sort1.6× faster than Rust

map_insert1.7× faster than Rust

dispatch2.1× faster than Rust

strings1.6× faster than Rust

fib7% faster than Rust

nbody7% faster than Rust

Servers and builds

web server12% faster than Rust

server memory2.8× less than Rust

fetch()9% faster than Rust

compile2.3× faster than Rust

binary size10.5× smaller than Rust

3.0× faster than Rustbinary_trees: millions of small objects

Tov260 ms

Bun376 ms

Node449 ms

C688 ms

Rust791 ms

Shorter is better.

1.6× faster than Rustsort: 3M floats, with a comparator

Tov45 ms

Rust74 ms

C275 ms

Bun696 ms

Node1175 ms

Shorter is better.

1.7× faster than Rustmap_insert: 2M string keys

Tov284 ms

C476 ms

Rust490 ms

Bun823 ms

Node1178 ms

Shorter is better.

2.1× faster than Rustdispatch: 50M virtual calls

Tov36 ms

C76 ms

Rust77 ms

Bun109 ms

Node191 ms

Shorter is better.

1.6× faster than Ruststrings: format, join, split

Tov143 ms

Bun203 ms

Rust224 ms

C253 ms

Node519 ms

Shorter is better.

7% faster than Rustfib: recursive calls, overflow-checked

Tov416 ms

Rust444 ms

C490 ms

Bun942 ms

Node1485 ms

Shorter is better.

7% faster than Rustnbody: floating point

Tov210 ms

Rust224 ms

C312 ms

Bun512 ms

Node554 ms

Shorter is better.

12% faster than Rustweb server: JSON API, one core

Tov242k req/s

Rust216k req/s

Bun111k req/s

Longer is better.

2.8× less than Rustserver memory: serving that API

Tov1.6 MB

Rust4.4 MB

Bun16.6 MB

Shorter is better.

9% faster than Rustfetch(): 64 requests at once

Tov177k req/s

Rust162k req/s

Bun129k req/s

Node30k req/s

Longer is better.

2.3× faster than Rustcompile: a program to a binary

Tov42 ms

C49 ms

Rust98 ms

Shorter is better.

10.5× smaller than Rustbinary size: a program, stripped

Tov33 KB

C33 KB

Rust351 KB

Shorter is better.

Measured on an Apple M4 Max, and each program prints the same output in every language. Tov checks integer overflow by default; Rust's release builds don't. Method and every result.

×

Trying Tov on Cap

Cap is our open-source screen recorder. We've tried two parts of its video pipeline on Tov: a new encoder written in Tov, and the media server Cap already has, compiled by Tov as it is. Neither runs in Cap's production yet.

A screen encoder, written in Tov

Every Cap recording ends up as H.264 video. We wrote an encoder for it in Tov, tuned for what's on a screen, and compared it with x264 at the same picture quality. The encoder is on GitHub.

5.2×faster than x264, on average

Idle screen8.1×

Code, 4K8.0×

Busy UI, 60 fps7.2×

Slow typing6.9×

Busy UI6.5×

Dark mode6.5×

Code editor6.5×

Slides4.6×

Scrolling text4.4×

Webcam overlay4.2×

Dashboard3.5×

Full-motion video1.5×

46 MB

of memory at its peak (x264: 377)

561 KB

the whole encoder (x264: 1.3 MB)

12 screen recordings at 1080p, on one thread, against x264's veryfast preset. Every result.

The media server, unchanged

Cap's media server is a Bun app (Hono, zod and FFmpeg) that probes uploaded videos and pulls out their audio. Tov runs it without changing a line; the file below is the whole port. Cap still serves it with Bun.
