npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@gcu/atra

v0.1.0

Published

Fortran/Pascal-style language that compiles to WebAssembly. Supports SIMD, multi-memory, structured control flow. Library API + atrac CLI.

Readme

atra

Arithmetic TRAnspiler — wat, but for humans.

C was famously described as "portable assembly" — a thin layer over the PDP-11's instruction set that happened to compile everywhere. That was 1972. The machine had 56KB of memory, 16-bit words, and a register file you could count on one hand. Dennis Ritchie didn't design a language; he gave the hardware a syntax.

Fifty years later, there's a new virtual machine: WebAssembly. Stack-based, 4 numeric types (i32, i64, f32, f64), linear memory, no strings, no IO, no garbage collector. It runs inside every browser on earth. And its "assembly language" — WAT — looks like this:

(func $add (param $a f64) (param $b f64) (result f64)
  local.get $a
  local.get $b
  f64.add)

So: what would "C for WebAssembly" look like? Not C compiled to Wasm (that's Emscripten), but a language designed for Wasm the way C was designed for the PDP-11 — one that maps cleanly onto the virtual machine's actual semantics, doesn't try to be more than it is, and compiles in microseconds?

That exploration led here. The result turned out closer to Fortran than to C. Wasm's type system (four numbers, nothing else) and execution model (structured control flow, no goto) are a better fit for formula translation than for systems programming. The syntax ended up a Fortran/Pascal hybrid: begin...end blocks, := assignment, function/subroutine distinction, return by assigning to the function name, ! comments, ** exponentiation. The lineage is literal:

  • FORTRAN = FORmula TRANslator (1957, IBM 704 machine code)
  • ATRA = Arithmetic TRAnspiler (2025, WebAssembly bytecode)

Same idea, different virtual machine, ~70 years apart.

import { atra } from './atra.js';

const { spherical } = atra`
  function spherical(h, range, sill, nugget: f64): f64
  begin
    if (h == 0.0) then
      spherical := 0.0
    else if (h >= range) then
      spherical := nugget + sill
    else
      spherical := nugget + sill * (1.5 * h / range - 0.5 * (h / range)**3)
    end if
  end
`;

spherical(25.0, 80.0, 1.0, 0.1)

The tagged template compiles source to Wasm bytecode, instantiates the module, and returns exported functions. No toolchain, no build step, no external compiler, no dependencies. One JS file that turns formulas into native-speed bytecode at runtime.


The language

Four numeric types matching Wasm's value types: i32, i64, f32, f64. No strings, no booleans (use i32, 0 = false), no pointers, no heap. Structs are available via layout declarations (see below) — they describe memory layouts but don't introduce new types.

Functions and subroutines

Functions return a value. The return mechanism is the Fortran convention — assign to the function's own name:

function distance(x1, y1, x2, y2: f64): f64
var
  dx, dy: f64;
begin
  dx := x2 - x1
  dy := y2 - y1
  distance := sqrt(dx**2 + dy**2)
end

Subroutines have no return value. Use them for operations that write results into memory:

subroutine normalize(arr: array f64; n: i32)
var
  i: i32;
  sum: f64;
begin
  sum := 0.0
  for i := 0, n
    sum := sum + arr[i]
  end for
  for i := 0, n
    arr[i] := arr[i] / sum
  end for
end

Control flow

All structured — no goto, because Wasm doesn't have goto.

! if/else
if (x > 0.0) then
  result := x
else
  result := 0.0 - x
end if

! if-expression (ternary) — compiles to Wasm's if (result T)
sign := if (x < 0.0) then -1 else 1

! for loop (0-based, exclusive upper bound, always)
for i := 0, n
  arr[i] := arr[i] * scale
end for

! for with step (countdown)
for i := n - 1, -1, -1
  ...
end for

! while
while (error > tolerance)
  ...
end while

! do...while (body executes at least once)
do
  count += 1
while (count < limit)

! early return (guard clause)
if (b == 0.0) then
  call return(0.0)
end if

! tail call (constant stack space recursion)
tailcall factorial(n - 1, acc * n)

Builtins

Common math functions work without any import declaration — the compiler auto-imports them from Math.*: sin, cos, sqrt, ln, exp, abs, floor, ceil, pow, min, max, atan2.

Native Wasm operations (single instruction, zero call overhead): trunc, nearest, copysign, select, clz, ctz, popcnt, rotl, rotr, memory_size, memory_grow, memory_copy, memory_fill.

Type conversions use type names as functions: f64(i), i32(x). No implicit coercion — atra is explicit about everything.

Dotted names

Identifiers can contain dots. The compiler treats physics.gravity as a single flat name — no namespace machinery, just a naming convention that the JS side respects by nesting into objects:

function model.spherical(h, a, c1, c0: f64): f64
begin
  ...
end

function model.gaussian(h, a, c1, c0: f64): f64
begin
  ...
end
wasm.model.spherical(0.5, 1.0, 1.0, 0.1)  // nested access
wasm['model.spherical'](...)                // flat access also works

Function references

Functions can be passed by reference via call_indirect. A bare function name (without parens) evaluates to its table index:

function double(x: f64): f64
begin
  double := x * 2.0
end

function triple(x: f64): f64
begin
  triple := x * 3.0
end

function apply(f: function(x: f64): f64, x: f64): f64
begin
  apply := f(x)
end
const wasm = atra`...`;
wasm.apply(wasm.__table.double, 5.0)  // 10
wasm.apply(wasm.__table.triple, 5.0)  // 15

When any function is used as a reference, the exports include a __table mapping function names to their table indices.

Layouts

Layouts describe struct-like memory regions — named fields at known offsets, with automatic size and alignment computation. Variables typed as layout are i32 pointers into linear memory.

layout Vec3
  x, y, z: f64
end layout

layout Sphere
  center: Vec3
  radius: f64
end layout

function getRadius(s: layout Sphere): f64
begin
  getRadius := s.radius
end

Field access (s.radius, s.center.x) compiles to i32.load/f64.load at the computed offset. Fields follow C-style alignment: f64 fields align to 8 bytes, i32 to 4 bytes.

Packed layouts skip alignment padding:

layout packed Rec
  id: i32
  value: f64
end layout

Here value is at offset 4 (not 8), and __size is 12 (not 16).

Array fields embed fixed-count arrays of primitives or layouts:

layout Particle
  pos: f64[3]      ! 3 consecutive f64s (24 bytes)
  mass: f64
end layout

layout Mesh
  vertices: Vec3[4]  ! 4 embedded Vec3s (96 bytes)
  count: i32
end layout

Access array elements with p.pos[i]. For layout arrays, mesh.vertices[i] returns a pointer — assign it to a layout-typed local to access fields:

var v: layout Vec3
v := mesh.vertices[i]
x := v.x

Layout constants are available at compile time: Vec3.__size (24), Vec3.__align (8), Vec3.y (offset 8). These emit i32.const — zero runtime cost.

__layouts metadata is attached to the JS exports object. For scalar fields, the value is the byte offset; for array fields, it's { offset, count, elemSize }:

const m = atra({ memory })`...`;
m.__layouts.Vec3       // { x: 0, y: 8, z: 16, __size: 24, __align: 8 }
m.__layouts.Particle   // { pos: { offset: 0, count: 3, elemSize: 8 }, mass: 24, ... }

SIMD

Vector types f64x2, f32x4, i32x4, i16x8, i8x16 with lane operations. Arithmetic operators (+, -, *, /) are overloaded for vector types:

function test(a, b: f64): f64
var
  va, vb, vc: f64x2
begin
  va := f64x2.splat(a)
  vb := f64x2.splat(b)
  vc := va + vb
  test := f64x2.extract_lane(vc, 0)
end

API

Six entry points, from high-level to low-level.

atra`...` — tagged template

Full pipeline: parse, compile, instantiate, nest dotted exports, attach __table. This is the primary interface.

const { add, mul } = atra`
  function add(a, b: f64): f64
  begin
    add := a + b
  end

  function mul(a, b: f64): f64
  begin
    mul := a * b
  end
`;

Template interpolation inlines JS values as constants:

const SIZE = 256;
const { getSize } = atra`
  function getSize(): f64
  begin
    getSize := ${SIZE}.0
  end
`;

atra({ imports })`...` — curried form

Pass JS functions (or other atra modules) as WebAssembly imports. Atra infers an all-f64 signature from .length:

const { compute } = atra({ lerp: (a, b, t) => a + (b - a) * t })`
  function compute(a, b, t: f64): f64
  begin
    compute := lerp(a, b, t)
  end
`;

Nested objects are flattened to dotted names — { math: { lerp: fn } } becomes math.lerp in atra source.

atra.run(source, imports?) — string API

Same full pipeline as the tagged template, but takes a plain string. Use when source comes from a file, is generated dynamically, or doesn't need template interpolation:

const src = await fetch('kernels.atra').then(r => r.text());
const { spherical, gaussian } = atra.run(src);

With imports:

const { compute } = atra.run(`
  function compute(x: f64): f64
  begin
    compute := scale(x)
  end
`, { scale: (x) => x * 2 });

atra.compile(source)Uint8Array

Raw WebAssembly bytes. No instantiation, no export nesting, no __table. Use when you need the binary for manual instantiation, caching, or sending to a worker.

atra.parse(source) → AST

Returns the abstract syntax tree. Useful for tooling, analysis, or custom code generation.

atra.dump(source) → hex string

WebAssembly bytes as a space-separated hex string. For when you want to stare at the bytecode.


Composability

atra output is a plain object of exported functions. Spread it as imports to another atra compilation:

const lib = atra`
  function linalg.dot(a, b: f64): f64
  begin
    linalg.dot := a * b
  end

  function linalg.scale(x, s: f64): f64
  begin
    linalg.scale := x * s
  end
`;

const app = atra({ ...lib })`
  function compute(a, b, s: f64): f64
  begin
    compute := linalg.scale(linalg.dot(a, b), s)
  end
`;

app.compute(3.0, 4.0, 2.0)  // → 24.0

This works because the convention is symmetric: atra outputs nested objects (lib.linalg.dot) and accepts nested objects as imports ({ linalg: { dot: fn } }). Flat dotted keys ({ 'linalg.dot': fn }) also work. Same with atra.run:

const lib = atra.run(libSource);
const app = atra.run(appSource, { ...lib });

Memory

Single memory

Pass a WebAssembly.Memory to enable array parameters. Arrays are typed views into linear memory — array f64 is a base pointer into a Float64Array:

const memory = new WebAssembly.Memory({ initial: 1 }); // 64KB

const { dotProduct } = atra({ memory })`
  function dotProduct(a, b: array f64; n: i32): f64
  var
    i: i32;
    sum: f64;
  begin
    sum := 0.0
    for i := 0, n
      sum := sum + a[i] * b[i]
    end for
    dotProduct := sum
  end
`;

const f64 = new Float64Array(memory.buffer);
f64.set([1, 2, 3], 0);     // a at byte offset 0
f64.set([4, 5, 6], 100);   // b at byte offset 800
dotProduct(0, 800, 3)       // → 32 (1*4 + 2*5 + 3*6)

Array parameters are i32 byte offsets at the Wasm level. 2D indexing uses row-major layout: arr[i, cols, j] computes arr[i * cols + j].

Multi-memory

Multiple independent memory banks (Chrome 120+, Firefox 125+, Edge 120+). Each bank has its own 4 GB address space. Declare named banks with memory, then qualify array parameters with the bank name:

const spatial = new WebAssembly.Memory({ initial: 4 });
const grades  = new WebAssembly.Memory({ initial: 4 });

const wasm = atra({ memory: { spatial, grades } })`
  memory spatial
  memory grades

  subroutine set_grade(g: grades array f64; i: i32; v: f64)
  begin
    g[i] := v
  end

  function get_grade(g: grades array f64; i: i32): f64
  begin
    get_grade := g[i]
  end
`;

Locally declared memories (with page counts) are created by the module itself:

const wasm = atra`
  memory scratch: 100

  subroutine write(arr: scratch array f64; i: i32; v: f64)
  begin
    arr[i] := v
  end
`;

Feature detection: atra.hasMultiMemory is true if the runtime supports multi-memory. If >1 bank is declared but the runtime doesn't support it, compilation throws an error.


Ahead-of-time compilation (atrac)

The runtime API (atra, atra.run, atra.compile) compiles source to Wasm at runtime in the browser. atrac is the ahead-of-time counterpart — a Node.js CLI and library that produces standalone artifacts with no runtime dependency on the atra compiler.

CLI

atrac lib.atra                    # → lib.js    (JS module with embedded Wasm)
atrac lib.atra -o lib.wasm        # → raw Wasm binary
atrac --src lib.atra              # → lib.src.js (source distribution)
atrac a.atra b.atra               # → concatenate, compile as one unit
atrac -o out.js a.atra b.atra     # → explicit output name

Install globally via npm link from the repo root (the bin entry in package.json registers it), or run directly with node ext/atra/atrac.js.

JS bundle output

atrac lib.atra produces a standalone ES module with Wasm bytes inlined as a Uint8Array. Exports include instantiate() and memory helpers:

import { instantiate } from './lib.js';

const memory = new WebAssembly.Memory({ initial: 4 });
const lib = instantiate({ memory });
lib.alpack.dgesv(aPtr, bPtr, n, nrhs, ipivPtr, infoPtr);

The bundle handles Math.* imports, memory linking, and dotted-name nesting automatically. No atra compiler needed at runtime.

Memory helpers

Every bundle also exports general-purpose Wasm memory utilities:

import { instantiate, alloc, writeF64, writeI32, readF64, readI32, growMemory } from './lib.js';

const mem = new WebAssembly.Memory({ initial: 4 });
const lib = instantiate({ memory: mem });
const st = { off: 0 };  // allocator state — caller manages offset

const pX = alloc(st, 10);       // reserve 10 f64s (80 bytes), 8-byte aligned
const pN = alloc(st, 0, 5);     // reserve 5 i32s (20 bytes), 8-byte aligned
growMemory(mem, st.off);         // grow memory if allocator exceeds current size

writeF64(mem, pX, [1.0, 2.0, 3.0]);
writeI32(mem, pN, [10, 20]);
const vals = readF64(mem, pX, 3);   // Float64Array copy (safe across grow)
const ints = readI32(mem, pN, 2);   // Int32Array copy
  • alloc(state, nf64, ni32) — bump allocator; takes { off } state object, returns byte offset
  • readF64 / readI32 — return copies (.slice), safe across grow operations
  • growMemory(mem, off) — grows memory only if off exceeds current buffer size

Future: a higher-level WasmMem manager class that wraps state + memory into a single object.

Source distribution

atrac --src lib.atra produces a JS module exporting routine strings for ${} interpolation:

import { sources, deps, all } from './lib.src.js';
import { alpack_dgetrf } from './lib.src.js';  // individual routines

// Use with std.include() for dependency resolution
const src = std.include({ sources, deps }, 'alpack.dgesv');
const { alpack } = atra({ memory })`${src}`;

Library API

atrac.js is also importable:

import { compile, bundle, buildSrc, formatSrcJs } from './ext/atra/atrac.js';

compile(source)            // → Uint8Array (raw Wasm bytes)
bundle(source, { name })   // → string (standalone JS module, with __layouts if applicable)
buildSrc(source)           // → { sources, deps, all, layouts? }
formatSrcJs(lib)           // → string (.src.js file content, with layouts export if applicable)

buildSrc() extracts routines and layout blocks, scans call graphs for dependencies, and returns the structured library object. If the source contains layouts, the result includes a layouts property mapping layout names to their source text. formatSrcJs() serializes routines, dependencies, and layouts as a JS module. bundle() embeds layout metadata (from atra.parse()) alongside the Wasm bytes — the instantiate() export attaches __layouts to the returned exports object. These are the same functions used by ext/atra/build.js to generate alpack.src.js and alpack.js.


Language reference

See SPEC.md for the full language specification — types, operators, control flow, arrays, SIMD, function references, the wasm.* escape hatch, and everything else.