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

bunaptic

v0.1.0-alpha.1

Published

Bun-first neural network and neuroevolution engine with Workers, Rust WASM, SIMD, dense fast paths, recurrent training, and Node fallback.

Readme

Bunaptic

Bunaptic is a Bun-first neural-network and neuroevolution engine inspired by the flexible ideas behind Neataptic, rebuilt around flat typed arrays, Workers, Rust WebAssembly, SIMD-capable kernels, dense feed-forward fast paths, recurrent sequence helpers, and a Node.js fallback for server and desktop runtimes.

This package is intentionally not a browser-era compatibility layer. It targets modern ESM runtimes where numeric workloads can benefit from typed arrays, worker threads, and packaged WASM artifacts.

Alpha Status

Current package version: 0.1.0-alpha.1.

Bunaptic is a production-oriented alpha. The core API, package layout, smoke checks, benchmark harnesses, and release artifacts are in place, but the project is pre-1.0 and should be treated honestly:

  • It is not battle-tested across many external applications yet.
  • It is not a drop-in replacement for every Neataptic API.
  • It does not target browsers, CDN script tags, AMD, or a global window build.
  • It does not ship native BLAS bindings.
  • SIMD is real, but it accelerates dot-product and reduction-heavy sections; nonlinear activation functions still run scalar lane math.
  • Recurrent WASM training currently supports sgd and momentum; recurrent adam and rmsprop use the TypeScript BPTT path.

The goal for alpha is to make the honest server/desktop rewrite usable, measurable, and safe to iterate on.

Relationship To Neataptic

Bunaptic exists because Neataptic proved that flexible JavaScript neural-network and neuroevolution APIs are useful, hackable, and fun. This project is explicitly inspired by those ideas.

It is also intentionally a new implementation:

  • Bunaptic is not an official Neataptic project.
  • Bunaptic is not affiliated with or endorsed by the original Neataptic maintainers.
  • Bunaptic is not a GitHub fork-style continuation of the original codebase.
  • Bunaptic is not a full drop-in replacement for every Neataptic API.
  • Bunaptic does include migration helpers and benchmarks for supported Neataptic JSON models.

The design goal is different: keep the flexible neural-network/neuroevolution spirit, but rebuild the runtime for Bun, Node.js, typed arrays, Workers, Rust WASM, SIMD-aware kernels, dense fast paths, and server/desktop workloads.

Table Of Contents

Install

bun add bunaptic

For npm consumers after publication:

npm install bunaptic

Bun is the primary runtime. Node.js is supported as a fallback for server/desktop workloads.

Runtime Targets

Supported targets:

  • Bun >=1.2.0
  • Node.js >=20.0.0
  • ESM import style
  • Server-side and desktop runtime workloads
  • Worker-based population evaluation
  • Packaged Rust WASM artifacts

Non-targets:

  • Browser bundle as a first-class distribution target
  • CDN script tag builds
  • AMD/CommonJS builds
  • window.Bunaptic globals
  • GPU acceleration
  • Native BLAS acceleration

Root import is the main public API:

import { Architect, Network, Neat, evaluatePopulation, loadWasmKernel } from "bunaptic";

Runtime marker imports are available when you want to be explicit:

import { runtimeTarget as bunTarget } from "bunaptic/bun";
import { runtimeTarget as nodeTarget } from "bunaptic/node";

console.log(bunTarget, nodeTarget);

Quick Start

Train a small XOR network:

import { Architect } from "bunaptic";

const set = [
  { input: [0, 0], output: [0] },
  { input: [0, 1], output: [1] },
  { input: [1, 0], output: [1] },
  { input: [1, 1], output: [0] },
];

const network = Architect.Perceptron(2, 6, 1);
const result = network.train(set, {
  iterations: 1000,
  rate: 0.5,
  momentum: 0.1,
  shuffle: true,
});

console.log(result);
for (const sample of set) {
  console.log(sample.input, Array.from(network.activate(sample.input)));
}

Use the WASM kernel:

import { Architect, Network, loadWasmKernel } from "bunaptic";

const kernel = await loadWasmKernel({ simd: "auto" });
const model = Architect.Perceptron(2, 8, 1);
const fast = Network.fromJSON(model.toJSON()).useWasm(kernel);

console.log(kernel.simdEnabled);
console.log(Array.from(fast.activate([0.2, 0.8])));

Core Concepts

Bunaptic has two execution layers:

  • TypeScript orchestration for public API, graph construction, mutation, serialization, dataset packing, recurrent specs, and high-level training decisions.
  • Rust WASM kernels for hot numeric paths such as graph forward passes, dense forward/evaluation, MSE reductions, same-topology population scoring, fused recurrent inference, and selected training paths.

Important internal representation choices:

  • Networks compile to flat typed arrays.
  • Connections are stored as indexed ranges instead of object graph traversal in hot loops.
  • Datasets can be packed once into contiguous Float32Array buffers.
  • Same-topology populations can be packed and scored together.
  • Dense layered graphs can bypass the generic graph walker.
  • LSTM/GRU builders attach a fused recurrent spec while keeping JSON/graph compatibility.

Public API Map

Common exports:

import {
  Architect,
  GraphBuilder,
  MutationMethod,
  Neat,
  Network,
  Rng,
  WorkerPool,
  cost,
  denseEvaluateDataset,
  denseForward,
  deriveDensePlan,
  evaluatePopulation,
  loadWasmKernel,
  packDataset,
  packDensePlan,
  packSequenceDataset,
  trainDensePlan,
} from "bunaptic";

High-level APIs to start with:

  • Architect.Perceptron(...) for feed-forward networks.
  • Architect.LSTM(...) and Architect.GRU(...) for fused recurrent sequence models.
  • Network.train(...) and Network.test(...) for feed-forward training and evaluation.
  • Network.trainSequences(...) and Network.testSequences(...) for fused recurrent training/evaluation.
  • packDataset(...) and packSequenceDataset(...) for reusable numeric buffers.
  • loadWasmKernel(...) for Rust WASM acceleration.
  • evaluatePopulation(...) for direct/worker population scoring.
  • Neat for dataset-backed or custom-objective evolution.

Advanced APIs:

  • GraphBuilder
  • low-level dense plan helpers
  • low-level WasmKernel handles
  • binary model helpers
  • topology helpers

These are exported because they are useful, but expect more pre-1.0 iteration around lower-level ergonomics.

Network API

Create and run a network:

const network = Architect.Perceptron(3, 12, 4, 2);
const output = network.activate([0.1, 0.5, 0.9]);

Train and test:

const result = network.train(trainingSet, {
  iterations: 500,
  error: 0.01,
  rate: 0.3,
  optimizer: "momentum",
  momentum: 0.1,
  shuffle: true,
});

const score = network.test(testSet);

Use packed datasets:

const packed = packDataset(trainingSet, network.input, network.output);
network.train(packed, { iterations: 500, rate: 0.3 });
network.testPacked(packed);

Attach or detach WASM acceleration:

const kernel = await loadWasmKernel();
network.useWasm(kernel);
network.activate([0.2, 0.8]);
network.useWasm(null);

Copy parameters:

const params = network.getParameters();
const clone = Network.fromJSON(network.toJSON()).setParameters(params);

Mutate:

network.mutate(MutationMethod.ModWeight);
network.mutate(MutationMethod.AddNode);
network.mutate(MutationMethod.AddConnection);

Stateful graph sequence stepping:

const state = network.createState();
const first = network.step([0.1, 0.2], state);
const second = network.step([0.3, 0.4], state);
network.clearState(state);

Architect API

Feed-forward:

const mlp = Architect.Perceptron(2, 8, 4, 1);

Random graph:

const random = Architect.Random(4, 2, {
  hidden: 10,
  connections: 24,
  recurrent: 2,
  seed: 42,
});

Recurrent-style graph builders:

const rnn = Architect.RNN(2, 8, 1);
const narx = Architect.NARX(2, 1, 8, 2, 2);
const hopfield = Architect.Hopfield(6);

Fused LSTM/GRU specs:

const lstm = Architect.LSTM(8, 32, 4, { seed: 42 });
const gru = Architect.GRU(8, 32, 4, { seed: 42 });

The fused recurrent builders keep a graph representation, but sequence inference and sequence training can use specialized TypeScript/WASM recurrent paths.

Datasets

Plain dataset shape:

const set = [
  { input: [0, 0], output: [0] },
  { input: [0, 1], output: [1] },
];

Packed dataset shape:

const packed = packDataset(set, 2, 1);

console.log(packed.inputSize);
console.log(packed.outputSize);
console.log(packed.sampleCount);
console.log(packed.inputs instanceof Float32Array);
console.log(packed.targets instanceof Float32Array);

Packed datasets are useful because they:

  • avoid repeated conversion of user arrays;
  • are transferable/cachable across Workers;
  • can be copied into WASM memory once for repeated scoring;
  • provide shape validation in one place.

Dense Plans

Strictly layered feed-forward graphs can be represented as dense plans. Architect.Perceptron(...) usually qualifies.

Dense plan requirements:

  • feed-forward only;
  • no recurrent/self connections;
  • no gated connections;
  • no constant nodes;
  • each layer must be fully connected from the previous layer;
  • all nodes in a layer must share an activation function.

Manual dense usage:

import { Architect, denseEvaluateDataset, denseForward, deriveDensePlan, packDataset } from "bunaptic";

const model = Architect.Perceptron(2, 8, 4, 1);
const plan = deriveDensePlan(model.compiledGraph());

if (plan) {
  const output = denseForward(plan, new Float32Array([0.25, 0.75]));
  const error = denseEvaluateDataset(plan, packDataset(trainingSet, 2, 1));
  console.log(output, error);
}

Automatic dense routing:

  • Network.activate(...) uses dense TS/WASM path when eligible.
  • Network.testPacked(...) uses dense TS/WASM dataset evaluation when eligible.
  • Network.train(...) can use dense TS training for eligible graphs.
  • If mutation/import creates arbitrary topology, Bunaptic falls back to the generic graph path.

WASM dense usage:

const kernel = await loadWasmKernel({ simd: "auto" });
const plan = deriveDensePlan(model.compiledGraph());

if (plan) {
  kernel.denseForwardPlan(plan, new Float32Array([0.25, 0.75]));
  kernel.denseForwardBatchPlan(plan, packed.inputs);
  kernel.denseEvaluatePackedDataset(plan, packed);
}

Rust WASM Kernel

The Rust kernel is packaged as two artifacts:

  • dist/bunaptic_kernel.wasm for scalar WASM.
  • dist/bunaptic_kernel_simd.wasm for simd128 WASM.

Default loading:

const kernel = await loadWasmKernel();

SIMD auto mode:

const kernel = await loadWasmKernel({ simd: "auto" });

Force scalar:

const scalar = await loadWasmKernel({ simd: false });

Require SIMD:

const simd = await loadWasmKernel({ simd: true });

Custom WASM location:

const kernel = await loadWasmKernel({ path: new URL("./bunaptic_kernel.wasm", import.meta.url) });

Runtime capability:

console.log(kernel.simdEnabled);

WASM-covered areas:

  • dense single forward;
  • dense plan forward;
  • dense plan batch forward;
  • dense packed dataset evaluation;
  • generic graph forward;
  • generic graph batch forward;
  • generic graph sequence forward;
  • generic graph ragged batch forward;
  • generic packed dataset MSE evaluation;
  • same-topology population V1/V2 scoring;
  • feed-forward graph training for sgd/momentum;
  • fused LSTM/GRU sequence inference;
  • fused LSTM/GRU ragged inference;
  • fused LSTM/GRU recurrent training for sgd/momentum.

The Rust source is split by responsibility:

rust/kernel/src/
├── lib.rs
├── memory.rs
├── math.rs
├── dense.rs
├── graph.rs
├── population.rs
└── recurrent.rs

The exported WASM ABI uses stable bunaptic_* symbol names consumed by src/wasm/kernel.ts.

SIMD Behavior

SIMD is real but scoped.

Currently accelerated:

  • dot-product accumulation;
  • MSE/squared-error reductions;
  • dense dot products through the shared dot helper;
  • recurrent gate/projection dot products where they use the shared dot helper;
  • lane-major population V2 four-genome chunk accumulation.

Still scalar:

  • sigmoid;
  • tanh;
  • sin;
  • exp-heavy activations;
  • most branch-heavy graph logic;
  • parts of recurrent backpropagation.

This means SIMD helps most on workloads with larger dot products, bigger datasets, dense networks, same-topology populations, and recurrent projection/gate computations. It is not a magic speedup for every tiny network.

Workers And Population Evaluation

Use evaluatePopulation(...) for ordered fitness scores without manually handling model cache, dataset cache, binary payloads, or Worker lifecycle.

import { Architect, evaluatePopulation, packDataset } from "bunaptic";

const population = Array.from({ length: 128 }, (_, index) =>
  Architect.Perceptron(2, 8 + (index % 4), 1),
);

const packed = packDataset(trainingSet, 2, 1);

const scores = await evaluatePopulation(population, packed, {
  mode: "auto",
  workers: 4,
  useWasm: true,
});

Modes:

  • direct: score on the current thread.
  • workers: always use a Worker pool.
  • auto: direct for small populations, Workers for larger populations.

When useWasm: true and cost is MSE:

  • same-topology feed-forward groups are evaluated through grouped WASM scoring;
  • population V2 uses lane-major weight/bias packing;
  • mixed topologies fall back per network while preserving result order;
  • recurrent graphs use per-network packed dataset evaluation.

Manual pool lifecycle:

import { WorkerPool, packDataset } from "bunaptic";

const pool = new WorkerPool({ size: 4, useWasm: true });
const packed = packDataset(trainingSet, 2, 1);

try {
  await pool.cacheDataset(packed);
  const scores = await pool.evaluate(population, packed);
  console.log(scores);
} finally {
  pool.terminate();
}

Always terminate manually created pools.

NEAT Evolution

Dataset-backed NEAT:

import { Neat, packDataset } from "bunaptic";

const neat = new Neat(2, 1, () => 0, {
  seed: 42,
  popsize: 64,
  elitism: 4,
  dataset: packDataset(trainingSet, 2, 1),
  evaluator: {
    mode: "auto",
    useWasm: true,
  },
});

for (let generation = 0; generation < 25; generation++) {
  const best = await neat.evolve();
  console.log(generation, best.score);
}

Custom objective:

const neat = new Neat(2, 1, async (network) => {
  const result = network.test(trainingSet);
  return -result.error;
});

Current NEAT scope:

  • mutation;
  • crossover;
  • innovation registry support;
  • deterministic seeded runs;
  • dataset-backed population evaluator;
  • worker/WASM scoring integration;
  • basic speciation/stagnation controls.

This is not a byte-for-byte Neataptic clone. It is a Bunaptic implementation of the same general family of ideas.

Recurrent Networks

Create a fused LSTM:

const model = Architect.LSTM(8, 32, 4, { seed: 42 });
const outputSteps = model.runSequence(new Float32Array(8 * 10));

Create a fused GRU:

const model = Architect.GRU(8, 32, 4, { seed: 42 });
const outputSteps = model.runSequence(new Float32Array(8 * 10));

Use WASM fused recurrent inference:

const kernel = await loadWasmKernel();
const fast = Network.fromJSON(model.toJSON()).useWasm(kernel);
const outputs = fast.runSequence(values);

Streaming continuation:

const state = fast.createRecurrentState();

fast.runSequence(chunkA, { recurrentState: state });
fast.runSequence(chunkB, { recurrentState: state });
fast.resetRecurrentState(state);

Ragged batches avoid padding every sequence to max length:

const outputs = fast.runBatchRagged({
  values: new Float32Array([
    1, 2,
    3, 4,
    5, 6,
  ]),
  offsets: new Uint32Array([0, 2, 6]),
  featureSize: 2,
});

The offsets array stores value offsets, not timestep counts.

Sequence Training

Final-output sequence sample:

const samples = [
  {
    input: [0, 1, 1, 2, 2, 3],
    length: 3,
    output: [1],
  },
];

Per-step sequence sample:

const samples = [
  {
    input: [0, 1, 1, 2, 2, 3],
    length: 3,
    output: [0, 1, 1],
  },
];

Pack sequences:

const packed = packSequenceDataset(samples, 2, 1, {
  targetMode: "auto",
});

Train with TypeScript BPTT:

model.trainSequences(packed, {
  optimizer: "adam",
  iterations: 100,
  rate: 0.05,
  clip: 1,
  truncatedSteps: 16,
});

Train with WASM recurrent path:

const kernel = await loadWasmKernel();
const fast = Network.fromJSON(model.toJSON()).useWasm(kernel);

fast.trainSequences(packed, {
  optimizer: "momentum",
  iterations: 100,
  rate: 0.02,
  momentum: 0.1,
  clip: 1,
});

Target modes:

  • final: output.length === outputSize
  • perStep: output.length === length * outputSize
  • auto: infer per sample, but mixed modes in one packed dataset are rejected

Optimizers

Supported optimizer names:

  • sgd
  • momentum
  • rmsprop
  • adam

Feed-forward training:

  • TypeScript path supports sgd, momentum, rmsprop, and adam.
  • WASM graph training supports sgd and momentum.
  • Dense TypeScript training supports all optimizer configs routed through the TS optimizer helper.

Recurrent training:

  • TypeScript BPTT supports sgd, momentum, rmsprop, and adam.
  • WASM recurrent training supports sgd and momentum.

If an optimizer is not supported by the WASM training path, Network.train(...) or Network.trainSequences(...) falls back to TypeScript where supported.

Serialization

JSON roundtrip:

const json = network.toJSON();
const restored = Network.fromJSON(json);

Binary roundtrip:

const bytes = network.toBinary();
const restored = Network.fromBinary(bytes);

Binary format notes:

  • Current encoder writes V2 binary payloads.
  • Decoder preserves V1 compatibility.
  • V2 stores connection innovations and fused recurrent specs.
  • The binary format is useful for worker payloads and package smoke checks, but it should still be considered pre-1.0.

Neataptic Migration

Supported feed-forward Neataptic JSON can be imported:

const bunapticModel = Network.fromNeatapticJSON(neatapticModel.toJSON());

Useful migration helpers:

const params = bunapticModel.getParameters();
bunapticModel.setParameters(params);

Migration honesty:

  • Bunaptic is inspired by Neataptic, not a complete API clone.
  • Browser-specific distribution patterns are intentionally omitted.
  • Unsupported activation names throw explicit errors.
  • Arbitrary imported topology may use generic graph paths instead of dense fast paths.

Performance Snapshot

This snapshot is from the maintainer machine and is meant to show the shape of the performance profile, not to make universal claims. Run the included benchmarks on your own workload before relying on the numbers.

Environment:

Profile: standard
CPU threads reported: 12
Bun: 1.3.x
Warmup: 2
Repeats: 7
Dataset comparison: 4096 samples
Inference comparison: 100000 activations
Population comparison: 512 genomes
SIMD artifact loaded: true

Best-practice Bunaptic setup for fast paths:

import { Network, loadWasmKernel, packDataset } from "bunaptic";

const kernel = await loadWasmKernel({ simd: "auto" });
const packed = packDataset(trainingSet, inputSize, outputSize);
const fast = Network.fromJSON(model.toJSON()).useWasm(kernel);

Use the right path for the workload:

| Workload | Recommended path | |---|---| | Tiny single inference calls | TypeScript graph/dense path | | Large inference batches | WASM dense batch path | | Dataset scoring | Packed dataset + WASM/dense dataset path | | Large feed-forward populations | evaluatePopulation / WorkerPool + grouped WASM V2 | | LSTM/GRU sequence inference | WASM recurrent handle path | | Recurrent sgd/momentum training | WASM recurrent training path | | Recurrent adam/rmsprop training | TypeScript BPTT path |

Selected Neataptic comparison rows from BENCH_PROFILE=standard bun run tests/neataptic-speed-benchmark.ts:

| Area | Neataptic median | Best Bunaptic median | Approx result | |---|---:|---:|---:| | Feed-forward training | 706.20 ms | 369.32 ms WASM train | ~1.9x faster | | Inference loop | 36.42 ms activate | 13.73 ms dense WASM batch | ~2.7x faster | | Inference noTrace baseline | 17.55 ms noTrace | 13.73 ms dense WASM batch | ~1.3x faster | | Dataset evaluation | 0.87 ms test | 0.56 ms WASM/dense dataset | ~1.5x faster | | Population scoring | 381.16 ms single | 123.12 ms workers+WASM | ~3.1x faster | | LSTM sequence inference | 124.91 ms | 6.61 ms WASM handle | ~18.9x faster | | GRU sequence inference | 2192.13 ms | 4.65 ms WASM handle | very large speedup in this benchmark |

Important caveats:

  • The MLP rows use the same initial Neataptic JSON imported into Bunaptic where applicable.
  • The recurrent rows compare constructor-level workloads, not identical recurrent internals.
  • The GRU speedup is huge because Neataptic's object-graph recurrent workload is very expensive in this benchmark and Bunaptic uses a fused recurrent spec plus a persistent WASM handle.
  • Calling WASM once per tiny activate() can be slower than TypeScript because JS-to-WASM boundary cost dominates.
  • Worker paths are best for larger populations/datasets; small workloads may be faster on the direct TypeScript path.
  • SIMD helps dot-product/reduction-heavy paths, but grouped layout, packed datasets, dense paths, and persistent handles are often more important than SIMD alone.

Bunaptic standard benchmark highlights from bun run bench and bun run bench:recurrent:

| Area | Slower path median | Faster path median | Approx result | |---|---:|---:|---:| | Graph dataset scalar WASM vs SIMD | 0.1018 ms | 0.0670 ms | SIMD ~1.5x faster | | Graph dataset scalar WASM vs dense SIMD | 0.1018 ms | 0.0539 ms | dense SIMD ~1.9x faster | | LSTM long sequence TS vs WASM handle | 362.77 ms | 26.53 ms | ~13.7x faster | | GRU long sequence TS vs WASM handle | 284.20 ms | 18.56 ms | ~15.3x faster | | LSTM ragged TS vs WASM handle | 239.98 ms | 17.30 ms | ~13.9x faster | | GRU ragged TS vs WASM handle | 188.96 ms | 11.98 ms | ~15.8x faster | | GRU recurrent train TS vs WASM | 0.6956 ms | 0.1784 ms | ~3.9x faster |

The honest summary: Bunaptic is strongest when you batch work, pack datasets, reuse handles, and let dense/WASM/Worker paths do the heavy lifting. It is not automatically faster for every tiny call.

Benchmarks

Benchmark scripts:

bun run bench
bun run bench:recurrent

Profiles:

BENCH_PROFILE=ci bun run bench
BENCH_PROFILE=quick bun run bench
BENCH_PROFILE=standard bun run bench
BENCH_PROFILE=large bun run bench

Machine-readable output:

BENCH_PROFILE=ci BENCH_FORMAT=json bun run bench
BENCH_PROFILE=ci BENCH_FORMAT=csv bun run bench:recurrent

Benchmark metadata includes runtime, profile, population/sample sizes, and SIMD capability when relevant.

Benchmark honesty:

  • Results depend heavily on CPU, runtime, network shape, dataset size, and whether SIMD is supported.
  • Tiny networks can be dominated by JS/WASM boundary cost.
  • Worker speedups depend on population size and worker startup/cache behavior.
  • SIMD speedups are strongest on dot/reduction-heavy workloads.
  • Use the included scripts on your target machine before making performance claims.

Parent repository comparison benchmark:

BENCH_PROFILE=ci bun run tests/neataptic-speed-benchmark.ts

That benchmark lives outside this package folder in the broader workspace and compares selected Neataptic/Bunaptic scenarios. It is useful for development, but it is not a universal claim that every workload is faster.

Validation

Release gate used during alpha hardening:

bunx tsc --noEmit
bun test
bun run build
bun run smoke
BENCH_PROFILE=ci bun run bench
BENCH_PROFILE=ci bun run bench:recurrent

Examples gate:

bun run examples/xor.ts
bun run examples/sequence.ts
bun run examples/wasm-graph.ts
bun run examples/worker-population.ts
bun run examples/neat-xor.ts
bun run examples/lstm-sequence.ts
bun run examples/gru-sequence.ts
bun run examples/recurrent-training.ts
bun run examples/dense-simd.ts
bun run examples/per-step-sequence.ts

Package smoke checks verify:

  • Bun import from built package;
  • Node import from built package;
  • worker entrypoint presence;
  • WASM artifact presence;
  • packed tarball consumption.

Latest alpha hardening snapshot on the maintainer machine:

TypeScript check: passed
Tests: 70 pass, 0 fail, 954 expect() calls across 8 files
Build: passed, 4 entrypoints plus declarations, scalar WASM, and SIMD WASM
Bun smoke: passed, direct=8, workers=8
Node smoke: passed, direct=4, workers=4
Package smoke: passed, bunaptic-0.1.0-alpha.1.tgz
CI benchmark: passed
CI recurrent benchmark: passed
Examples: passed
Parent Neataptic comparison CI benchmark: passed
Rust scalar/SIMD build warnings: none observed in the release gate

Do not treat validation on one machine as proof of all environments. It is a release gate, not a mathematical guarantee.

Examples

Available examples:

  • examples/xor.ts — basic XOR training.
  • examples/sequence.ts — dynamic sequence input without max-length padding.
  • examples/wasm-graph.ts — generic graph WASM path.
  • examples/worker-population.ts — worker population evaluation.
  • examples/neat-xor.ts — dataset-backed NEAT loop.
  • examples/lstm-sequence.ts — fused LSTM sequence inference.
  • examples/gru-sequence.ts — fused GRU ragged sequence inference.
  • examples/recurrent-training.ts — TypeScript and WASM recurrent training.
  • examples/dense-simd.ts — dense plan + SIMD kernel usage.
  • examples/per-step-sequence.ts — per-step sequence target training.

Run one example:

bun run examples/dense-simd.ts

Package Contents

Published package files are intended to include:

  • dist/index.js
  • dist/bun.js
  • dist/node.js
  • dist/**/*.d.ts
  • dist/runtime/evaluate-worker.js
  • dist/bunaptic_kernel.wasm
  • dist/bunaptic_kernel_simd.wasm
  • README.md
  • LICENSE

The package is ESM-only.

Development

Install dependencies:

bun install

Install the Rust WASM target if missing:

rustup target add wasm32-unknown-unknown

Typecheck:

bunx tsc --noEmit

Test:

bun test

Build JS declarations plus scalar/SIMD WASM:

bun run build

Smoke test built artifacts:

bun run smoke

Run the compact release check:

bun run check

Build script details:

  • cleans dist;
  • builds scalar Rust WASM;
  • builds SIMD Rust WASM with RUSTFLAGS="-C target-feature=+simd128";
  • bundles JS entries with Bun;
  • emits TypeScript declarations;
  • copies WASM artifacts into dist.

Limitations

Current limitations are intentional and documented:

  • Browser/CDN builds are not a target.
  • CommonJS is not a target.
  • Native BLAS is not included.
  • GPU acceleration is not included.
  • SIMD does not vectorize every activation or every recurrent operation.
  • WASM graph training is limited to feed-forward sgd/momentum.
  • WASM recurrent training is limited to LSTM/GRU sgd/momentum.
  • Recurrent adam/rmsprop training uses the TypeScript BPTT path.
  • Arbitrary mutated/imported graphs may fall back from dense paths.
  • Pre-1.0 advanced APIs may change.
  • Benchmarks are workload-specific and should be rerun on target hardware.

Roadmap

Likely future work:

  • recurrent WASM optimizer state for adam and rmsprop;
  • dense training WASM kernel;
  • fewer recurrent BPTT allocations;
  • deeper fuzz/property tests for graph mutations and serialization;
  • more Neataptic import coverage where practical;
  • more benchmark scenarios and hardware notes;
  • optional browser-focused package if there is a real need;
  • 1.0 API freeze after external usage feedback.

Not planned by default:

  • legacy browser compatibility layers;
  • AMD/global builds;
  • hard dependency on a heavy numeric runtime;
  • pretending the alpha has been battle-tested at 1.0 scale.

FAQ

Is Bunaptic a drop-in replacement for Neataptic?

No. It is a rewrite inspired by Neataptic-style flexibility and neuroevolution, but with a different runtime target and a different performance architecture.

Why Bun-first?

Bun gives a modern TypeScript-friendly runtime, fast startup, built-in test runner, good ESM behavior, and a practical server/desktop target. Node fallback is kept for package consumers.

Why Rust WASM instead of a full Rust rewrite?

The public API and orchestration are easier to iterate in TypeScript. Rust is used where it helps most: numeric kernels and memory-local hot loops.

Why not browser support?

The project is focused on server/desktop workloads, Workers, package-safe WASM loading, and Bun/Node behavior. Browser support would add compatibility constraints that are not part of the alpha goal.

Is SIMD always faster?

No. SIMD helps on dot/reduction-heavy workloads, but tiny networks and boundary-heavy calls may not benefit. Measure your workload.

Can I publish this package now?

The package is structured for alpha publication, but publishing is an explicit release decision. Run the release gate and inspect the tarball before publishing.

License

MIT. See LICENSE.