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.
Maintainers
Keywords
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
windowbuild. - 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
sgdandmomentum; recurrentadamandrmspropuse 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
- Relationship To Neataptic
- Runtime Targets
- Quick Start
- Core Concepts
- Public API Map
- Network API
- Architect API
- Datasets
- Dense Plans
- Rust WASM Kernel
- SIMD Behavior
- Workers And Population Evaluation
- NEAT Evolution
- Recurrent Networks
- Sequence Training
- Optimizers
- Serialization
- Neataptic Migration
- Performance Snapshot
- Benchmarks
- Validation
- Examples
- Package Contents
- Development
- Limitations
- Roadmap
- FAQ
- License
Install
bun add bunapticFor npm consumers after publication:
npm install bunapticBun 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.Bunapticglobals- 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
Float32Arraybuffers. - 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(...)andArchitect.GRU(...)for fused recurrent sequence models.Network.train(...)andNetwork.test(...)for feed-forward training and evaluation.Network.trainSequences(...)andNetwork.testSequences(...)for fused recurrent training/evaluation.packDataset(...)andpackSequenceDataset(...)for reusable numeric buffers.loadWasmKernel(...)for Rust WASM acceleration.evaluatePopulation(...)for direct/worker population scoring.Neatfor dataset-backed or custom-objective evolution.
Advanced APIs:
GraphBuilder- low-level dense plan helpers
- low-level
WasmKernelhandles - 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.wasmfor scalar WASM.dist/bunaptic_kernel_simd.wasmforsimd128WASM.
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.rsThe 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 === outputSizeperStep:output.length === length * outputSizeauto: infer per sample, but mixed modes in one packed dataset are rejected
Optimizers
Supported optimizer names:
sgdmomentumrmspropadam
Feed-forward training:
- TypeScript path supports
sgd,momentum,rmsprop, andadam. - WASM graph training supports
sgdandmomentum. - Dense TypeScript training supports all optimizer configs routed through the TS optimizer helper.
Recurrent training:
- TypeScript BPTT supports
sgd,momentum,rmsprop, andadam. - WASM recurrent training supports
sgdandmomentum.
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: trueBest-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:recurrentProfiles:
BENCH_PROFILE=ci bun run bench
BENCH_PROFILE=quick bun run bench
BENCH_PROFILE=standard bun run bench
BENCH_PROFILE=large bun run benchMachine-readable output:
BENCH_PROFILE=ci BENCH_FORMAT=json bun run bench
BENCH_PROFILE=ci BENCH_FORMAT=csv bun run bench:recurrentBenchmark 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.tsThat 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:recurrentExamples 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.tsPackage 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 gateDo 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.tsPackage Contents
Published package files are intended to include:
dist/index.jsdist/bun.jsdist/node.jsdist/**/*.d.tsdist/runtime/evaluate-worker.jsdist/bunaptic_kernel.wasmdist/bunaptic_kernel_simd.wasmREADME.mdLICENSE
The package is ESM-only.
Development
Install dependencies:
bun installInstall the Rust WASM target if missing:
rustup target add wasm32-unknown-unknownTypecheck:
bunx tsc --noEmitTest:
bun testBuild JS declarations plus scalar/SIMD WASM:
bun run buildSmoke test built artifacts:
bun run smokeRun the compact release check:
bun run checkBuild 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/rmsproptraining 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
adamandrmsprop; - 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.
