A modern TypeScript NEAT library built to be read, tested, and extended.
NeatapticTS implements NeuroEvolution of Augmenting Topologies (NEAT) — an algorithm that discovers neural-network structure by evolution rather than by hand-design. Unlike gradient-based training, which assumes a fixed architecture and adjusts weights, NEAT simultaneously searches the space of topologies and the space of weights. Connections grow, nodes split, and entire species of structurally different solutions compete in the same population until a solution emerges that could not have been specified in advance.
This is not a black-box experiment runner. The library is built to be inspected, extended, and learned from: typed primitives, deterministic seeds, rich telemetry, and documentation that explains the why at every level — not just the API surface.
Most neural-network training starts by choosing an architecture — how many layers, how many neurons, what connections. NEAT removes that assumption. It starts with minimal networks and uses three coordinated mechanisms to search the combined space of weights and structure:
1. Historical markings — Every structural innovation (a new node or connection) receives a globally unique innovation number. When two genomes from different structural lineages are crossed over, historical markings allow meaningful alignment: genes with matching innovation numbers describe the same structural feature, even if the genomes grew their shared ancestry by different mutation paths.
2. Speciation — New topology is immediately at a disadvantage against well-tuned incumbents. NEAT groups genomes into species using a compatibility distance function, and fitness sharing ensures each species competes primarily against its own members rather than across topological families. This gives new structures the generations they need to prove useful before they are eliminated.
3. Minimal complexification — NEAT begins every run from the simplest possible network (inputs directly to outputs) and adds structure only when mutations suggest it. This keeps search in the tractable part of the topology space for as long as possible. See Stanley and Miikkulainen, Evolving Neural Networks through Augmenting Topologies, for the original paper that established the algorithm.
The speciation boundary between any two genomes is determined by measuring how structurally different they are. If two genomes share most innovation numbers, they belong in the same species. If many innovations are disjoint or excess, they likely represent different evolutionary lineages.
The compatibility distance δ between two genomes is:
δ = (c₁ · E) / N + (c₂ · D) / N + c₃ · W̄
where E = excess gene count, D = disjoint gene count, N = larger genome length (normalizes for size), W̄ = mean weight difference of matching genes, and c₁, c₂, c₃ are coefficients that tune the relative importance of each term. Two genomes belong to the same species when δ < threshold. See Wikipedia contributors, Neuroevolution of augmenting topologies, for a concise summary of the algorithm's mechanics.
Each generation of a NEAT run follows the same five-step cycle:
flowchart LR
subgraph Generation["One Generation"]
direction LR
Evaluate["Evaluate\nScore each genome\nagainst the task"]
Speciate["Speciate\nGroup by compatibility\ndistance δ"]
Select["Select\nFitness sharing\nwithin species"]
Reproduce["Reproduce\nCrossover + mutation\npreserving innovations"]
Grow["Grow\nAdd nodes/connections\nvia structural mutations"]
end
Evaluate --> Speciate --> Select --> Reproduce --> Grow --> Evaluate
classDef base fill:#001522,stroke:#0fb5ff,color:#9fdcff,stroke-width:2px;
classDef accent fill:#0f1f33,stroke:#00e5ff,color:#d8f6ff,stroke-width:2px;
class Evaluate,Speciate,Select,Reproduce,Grow base;
Many neuroevolution libraries are either convenient but opaque, or educational but too small to trust as a real reference. NeatapticTS is built to close that gap.
The project gives you:
- Readable internals — orchestration-first module structure, each boundary explained by its own chapter README.
- Deterministic reproducibility — seeded RNG, exportable state, replayable runs.
- Modern TypeScript — ES2023+ ergonomics, full type coverage, no runtime
any. - Rich telemetry — per-generation diversity, species history, Pareto fronts, novelty tracking.
- Worker-backed evaluation — parallel genome scoring for Node and browser environments.
- ONNX export — trained networks portable to ONNX-compatible inference runtimes.
- Educational examples — two full examples (Flappy Bird, ASCII Maze) that teach architecture choices rather than hiding them.
The library is organized into four cooperating layers. Reading them in order builds a complete picture from graph primitives up to the evolutionary controller:
flowchart TD
subgraph Public["Public API · src/neataptic.ts"]
Neat["Neat\nevolutionary controller"]
Network["Network\ngraph orchestration"]
Methods["methods\nactivation · cost · selection · mutation"]
end
subgraph Architecture["src/architecture/ · Graph primitives"]
Node["Node\nneuron with activation state"]
Connection["Connection\nweighted directed edge"]
Layer["Layer / Group\nstructured neuron sets"]
NetworkImpl["Network internals\nactivate · train · serialize · mutate · ONNX"]
end
subgraph NEAT["src/neat/ · Evolutionary controller"]
Init["init/\npopulation setup"]
Evaluate["evaluate/\nscoring and objectives"]
Evolve["evolve/\nselection and offspring"]
Speciation["speciation/\ncompatibility grouping"]
Mutation["mutation/\nstructural operators"]
Telemetry["telemetry/\ndiversity and lineage"]
RNG["rng/ export/ cache/\nreproducibility"]
end
subgraph Workers["src/multithreading/ · Parallel evaluation"]
BrowserWorker["Browser worker"]
NodeWorker["Node worker"]
end
Neat --> Init
Neat --> Evaluate
Neat --> Evolve
Neat --> Speciation
Neat --> Mutation
Neat --> Telemetry
Neat --> RNG
Network --> NetworkImpl
NetworkImpl --> Node
NetworkImpl --> Connection
NetworkImpl --> Layer
Evaluate --> Workers
classDef pub fill:#001522,stroke:#00e5ff,color:#d8f6ff,stroke-width:2px;
classDef arch fill:#001522,stroke:#0fb5ff,color:#9fdcff,stroke-width:1.5px;
classDef neat fill:#001522,stroke:#0fb5ff,color:#9fdcff,stroke-width:1.5px;
classDef work fill:#001522,stroke:#ff9a2e,color:#ffe6cc,stroke-width:1.5px;
class Neat,Network,Methods pub;
class Node,Connection,Layer,NetworkImpl arch;
class Init,Evaluate,Evolve,Speciation,Mutation,Telemetry,RNG neat;
class BrowserWorker,NodeWorker work;
| Goal | Best place to start |
|---|---|
| Read the library architecture from the source side | src/README.md |
| Study the strongest end-to-end example | examples/flappy_bird/README.md |
| Study curriculum learning and reward shaping | examples/asciiMaze/README.md |
| Browse runnable example source directly | examples |
| Review contribution standards | CONTRIBUTING.md and STYLEGUIDE.md |
- Read the background section above — especially the three key mechanisms and the compatibility distance formula.
- Open src/neat/README.md for the controller's public defaults and four-lane architecture.
- Skim examples/flappy_bird/README.md to see those concepts applied in a complete system.
- Read docs/index.html.
- Read src/README.md.
- Open one example:
- src/neat/README.md — the evolutionary controller
- src/architecture/network/README.md — the network graph
- src/architecture/network/onnx/README.md — ONNX portability
- src/multithreading/README.md — parallel evaluation
examples/flappy_bird is the best single example if you want to see how NeatapticTS feels in a real project. It combines deterministic environment stepping, evaluation designed to reduce lucky-rollout bias, worker-backed browser playback, live network inspection, and a modular architecture with explicit boundaries.
examples/asciiMaze is the companion example for studying curriculum progression, compact observations, reward shaping for sparse goals, and browser plus terminal visualization.
Runtime requirement: Node 22+.
npm install @reicek/neataptic-tsMinimal example — evolve a network toward a target output in one generation loop:
import { Neat } from '@reicek/neataptic-ts';
const fitness = (network) => {
const output = network.activate([1])[0];
return -(output - 2) ** 2;
};
const neat = new Neat(1, 1, fitness, {
popsize: 30,
seed: 42,
fastMode: true,
});
await neat.evaluate();
await neat.evolve();
console.log(neat.getBest()?.score);For options, telemetry, multiobjective search, ONNX export, and subsystem details, continue in docs/index.html or src/README.md.
| Path | Purpose |
|---|---|
| src | Core library code and generated module docs |
| examples | Educational examples and demos |
| docs | Generated documentation site and example assets |
| scripts | Build and docs tooling |
| plans | Architecture and roadmap material |
This repo treats documentation as part of the product. If you change behavior, examples, or public API shape, update the documentation surface that teaches that boundary.
Primary contribution entry points:
MIT.
