Para / ParaBun

Para

Para is two things: a set of TypeScript libraries (signals, parallel, pipeline, arena, simd, csv, arrow, rtp, mcp) and an optional .pts syntax that compiles to JS calls into them. The libraries are pure JS / Wasm and work in any JS runtime.

ParaBun is a separate runtime (Bun fork) that bundles Para and adds modules for GPU, camera, audio, GPIO/I²C/SPI, and on-device LLM.

Lib + Lang

Para Lib

Nine modules under the para:* import prefix. para:signals provides reactive cells, derived values, and effects. The other modules use it to expose their state — para:audio's capture stream has a peakLevel signal, para:gpio's line value is a signal, and so on. So effect() from para:signals works against any of them.

Each module is its own @para/* npm package — install only the ones you use. Install guide → · Module docs →

Para Lang

.pts / .pjs files compile to standard JavaScript that imports from Para Lib. Added forms:

  • pure / memo function declarators
  • signal x = 0 / effect { … } / x ~> y / x -> fn (reactive bindings)
  • defer EXPR / arena { … } blocks
  • |> pipelines, integer ranges (0..n, 0..=n)
  • ..! / ..& chained .catch / .finally

The reactive forms desugar to para:signals imports, so the language requires at least that module at runtime. The other libraries' JS APIs work without the language. Compiler today is bundled in ParaBun; standalone @para/transpile in development. Language reference →

Libraries (Para Lib)

Signals — reactive state

Cells, derived values, effects. The other libraries expose their state as signals, so reactive composition is uniform across the suite.

import { signal, derived, effect } from "@para/signals";

const count = signal(0);
const doubled = derived(() => count.get() * 2);

effect(() => console.log(count.get(), doubled.get())); // 0, 0

count.set(5);                  // logs: 5, 10
count.update(n => n + 1);      // logs: 6, 12

Parallel — work over a Worker pool

pmap / preduce ship pure functions to a persistent Worker pool via fn.toString(). Functions must be pure — no closures, no outer references.

import { pmap } from "@para/parallel";

function score(row) {
  let h = 0;
  for (let i = 0; i < row.length; i++) h = (h * 31 + row.charCodeAt(i)) | 0;
  return h * h;
}

const scores = await pmap(score, rows, { concurrency: 8 });

Pipeline — fused streaming combinators

Adjacent maps over Float32Array / Float64Array sources fuse into a single SIMD pass via @para/simd instead of allocating an intermediate per stage.

import { map, sum } from "@para/pipeline";

const arr = new Float32Array(1_000_000);
const total = await sum(map(x => x * 2)(map(x => x + 1)(arr)));
// One SIMD pass through @para/simd, not three.

SIMD — vector primitives over typed arrays

WebAssembly v128 kernels (simd.wasm) with a scalar JS fallback for hosts without v128.

import { add, dot, sum, mulScalar } from "@para/simd";

const a = new Float32Array([1, 2, 3, 4]);
const b = new Float32Array([5, 6, 7, 8]);

add(a, b);          // Float32Array([6, 8, 10, 12])
mulScalar(a, 3);    // Float32Array([3, 6, 9, 12])
dot(a, b);          // 70
sum(a);             // 10

Arrow — in-memory tables, IPC, Parquet

Columnar tables with vectorized computes, Arrow IPC streaming, Parquet read/write. Wire-compatible with apache-arrow 21.1.0.

import { fromRows, mean, toParquet } from "@para/arrow";

const t = fromRows([
  { id: 1, age: 30 },
  { id: 2, age: 25 },
]);

mean(t.column("age"));                                // 27.5
const buf = await toParquet(t, { compression: "snappy" });

Syntax (Para Lang)

Signals and effects

A signal declaration creates a reactive cell. Bare reads inside a tracked context (an effect, a derived, a when block, or another signal's RHS) compile to .get(). Bare writes compile to .set(). A signal whose initializer reads other signals is auto-promoted to a derived value.

signal count = 0;
signal doubled = count * 2;       // derived; recomputes when count changes

effect { console.log(doubled); }  // runs once now, again on each change

count++;                          // count.set(count.get() + 1)

Edge-triggered handlers

A when block fires its body once on each false→true transition of the predicate. when not fires on the true→false transition. The predicate is tracked the same way an effect body is.

signal score = 0;

when score >= 100 { unlockAchievement("century"); }
when not online   { showOfflineBanner(); }

Reactive bindings

A ~> B desugars to effect(() => { B = A; }), an assignment that stays in sync. A -> fn desugars to effect(() => { fn(A); }), a call binding. Both are shorthand for the common single-statement effect.

signal name = "world";
name ~> document.title;          // title tracks name
name -> console.log;             // logs on every change

Ranges and pipelines

a..b is an exclusive integer range; a..=b is inclusive. The pipeline operator |> threads a value through a sequence of unary calls.

for (const i of 0..n) work(i);
const evens = 0..=20 |> filter(i => i % 2 === 0);

const out = pixels |> map(p => p * 1.2) |> clamp(0, 255);

Error operators

..! is .catch. ..& is .finally. They chain naturally with await to flatten a try/catch block.

const data = await fetch(url).then(r => r.json())
  ..! err => defaults
  ..& ()  => spinner.hide();

Compilation

Para files (.pts) are parsed by ParaBun's transpiler. (Mainline Bun doesn't recognize the syntax — the parser additions are part of the ParaBun fork.) The output is standard JavaScript with a handful of imports from para:* module specifiers.

On ParaBun, those specifiers resolve to built-in modules. On any other host (browser, Node, Bun, Deno, Cloudflare Workers, …) alias them to the matching @para/* npm packages in your bundler.

// vite.config.ts
import { defineConfig } from "vite";

export default defineConfig({
  resolve: {
    alias: [{ find: /^para:(.*)$/, replacement: "@para/$1" }],
  },
});

The same one-line alias works for esbuild, webpack, and rollup. See the install guide for the variants.

$npm install @para/signals @para/parallel @para/pipeline
$parabun build src/main.pts --outdir dist/

ParaBun is required for the build step today (it owns the .pts parser). A standalone npm-installable transpiler (@para/transpile) is on the roadmap.

Examples

Three worked projects, one per host environment. Each is a complete project with file layout, source, and build commands.