Interactive Neural Network Decision Boundary Visualiser — a browser-based application that visualises how neural networks learn to classify 2D data points in real-time. Built with Hexagonal Architecture (Ports & Adapters) to demonstrate clean separation of concerns.
- Real-time Training Visualisation — Watch the decision boundary evolve as the network learns
- Multiple Datasets — Circle, XOR, Spiral, and Gaussian cluster patterns
- Configurable Hyperparameters — Adjust learning rate and hidden layer architecture
- Step-by-Step Mode — Debug training one epoch at a time
- Responsive UI — Modern dark theme with Tailwind CSS
NeuroViz follows Hexagonal Architecture (also known as Ports & Adapters), ensuring the core business logic is completely decoupled from infrastructure concerns.
┌─────────────────────────────────────────────────────────────────┐
│ Presentation │
│ (index.html, styles) │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Composition Root │
│ (main.ts) │
│ Wires adapters to ports via dependency injection │
└─────────────────────────────────────────────────────────────────┘
│
┌───────────────────┼───────────────────┐
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ TFNeuralNet │ │ D3Chart │ │ MockDataRepo │
│ (TensorFlow) │ │ (D3.js) │ │ (Mock API) │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
│ implements │ implements │ implements
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ INeuralNetwork │ │ IVisualizer │ │ IDatasetRepo │
│ Service │ │ Service │ │ │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
└───────────────────┼───────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Core │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ Domain │ │ Ports │ │ Application │ │
│ │ (Entities) │ │ (Interfaces)│ │ (TrainingSession) │ │
│ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
src/
├── core/ # Framework-agnostic business logic
│ ├── domain/ # Entities: Point, Prediction, Hyperparameters
│ ├── ports/ # Interfaces: INeuralNetworkService, IVisualizerService
│ └── application/ # Use cases: TrainingSession orchestrator
│
├── infrastructure/ # Framework-specific implementations
│ ├── tensorflow/ # TFNeuralNet adapter (TensorFlow.js)
│ ├── d3/ # D3Chart adapter (D3.js)
│ └── api/ # MockDataRepository (simulated microservice)
│
├── presentation/ # UI styles (Tailwind CSS)
└── main.ts # Composition root (dependency injection)
| Decision | Rationale |
|---|---|
| Ports & Adapters | Core logic has zero dependencies on TensorFlow.js or D3.js |
| Constructor Injection | All dependencies injected via TrainingSession constructor |
| Async Training Loop | Guard-rail pattern prevents overlapping GPU calls |
| Immutable Domain | Point, Prediction, Hyperparameters are readonly |
- Node.js 20+ (LTS recommended)
- npm 10+
# Clone the repository
git clone https://github.com/DevilsDev/NeuroViz.git
cd NeuroViz
# Install dependencies
npm install
# Start development server
npm run devThe app will open at http://localhost:3000.
| Command | Description |
|---|---|
npm run dev |
Start development server with hot reload |
npm run build |
Build for production |
npm run preview |
Preview production build locally |
npm run typecheck |
Run TypeScript type checking |
npm test |
Run unit tests (Vitest) |
npm run test:coverage |
Run tests with coverage report |
npm run test:e2e |
Run E2E tests (Playwright) |
npm run test:e2e:ui |
Run E2E tests with interactive UI |
- Select a Dataset — Choose from Circle, XOR, Spiral, or Gaussian
- Fetch Data — Click "Fetch Data" to load the dataset (simulates API call)
- Configure Network — Set learning rate and hidden layer sizes (e.g.,
8, 4) - Initialise — Click "Initialise Network" to create the model
- Train — Click "Start" to begin training, or "Step" for single epochs
- Observe — Watch the decision boundary evolve in real-time
| Layer | Technology |
|---|---|
| ML Framework | TensorFlow.js |
| Visualisation | D3.js |
| Styling | Tailwind CSS |
| Build Tool | Vite |
| Unit Testing | Vitest |
| E2E Testing | Playwright |
| Language | TypeScript 5.6 |
Core domain and application logic tested with mocked infrastructure:
npm testCoverage focuses on:
TrainingSessionorchestration logic- Domain entity validation
- Port contract compliance
Full browser tests across Chromium, Firefox, and WebKit:
npm run test:e2eTest categories:
- Happy Path — Full training cycle, pause/resume, reset
- Mocked Microservice — Deterministic data for reproducible tests
- Error Handling — Input validation, disabled states
- Accessibility — Button labels, keyboard navigation
The GitHub Actions workflow runs on every push and PR:
- Lint & Type Check — TypeScript compilation
- Unit Tests — Vitest with coverage
- Build — Vite production build
- E2E Tests — Playwright across 3 browsers
- Deploy — GitHub Pages (main branch only)
See docs/ROADMAP.md for planned features including:
- Advanced optimizers (Adam, RMSprop) and regularization
- Real-time training metrics and loss charts
- Custom dataset upload and drawing
- Multi-class classification support
- Model export and session persistence
- Educational tooltips and tutorials
- Create a new adapter implementing
INeuralNetworkService:
// src/infrastructure/onnx/ONNXNeuralNet.ts
export class ONNXNeuralNet implements INeuralNetworkService {
async initialize(config: Hyperparameters): Promise<void> { /* ... */ }
async train(data: Point[]): Promise<number> { /* ... */ }
async predict(grid: Point[]): Promise<Prediction[]> { /* ... */ }
}- Swap the adapter in
main.ts:
// const neuralNetService = new TFNeuralNet();
const neuralNetService = new ONNXNeuralNet();No changes required to TrainingSession or any core logic.
- Implement
IVisualizerService:
// src/infrastructure/canvas/CanvasChart.ts
export class CanvasChart implements IVisualizerService {
renderData(points: Point[]): void { /* ... */ }
renderBoundary(predictions: Prediction[], gridSize: number): void { /* ... */ }
}- Inject in
main.ts:
const visualizerService = new CanvasChart('viz-container', 500, 500);Apache 2.0 DevilsDev
See LICENSE for details.