JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 34
  • Score
    100M100P100Q80785F
  • License Apache-2.0

TypeScript/JavaScript bindings for LOOM neural network framework with WebAssembly support - GPU-accelerated machine learning in the browser

Package Exports

  • @openfluke/welvet
  • @openfluke/welvet/dist/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@openfluke/welvet) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

@openfluke/welvet

TypeScript/JavaScript bindings for the LOOM neural network framework via WebAssembly

Welvet (Wrapper for Embedding Loom Via External Toolchain) provides a complete neural network API in the browser and Node.js/Bun environments. Built on WebAssembly, it delivers high-performance deep learning with zero dependencies.

npm version License

✨ Features

  • 🚀 5.4MB WASM Binary - Complete neural network framework compiled to WebAssembly
  • 🧠 All 5 Layer Types - Dense, Conv2D, Multi-Head Attention, RNN, LSTM fully supported
  • 🎯 Registry-based Initialization - Dynamic layer creation via CallLayerInit() with zero manual exports
  • 🔍 Runtime Introspection - Discover methods, signatures, and parameters dynamically
  • 💾 Model Serialization - Save/load models as JSON (no filesystem required)
  • Full Training Support - Train networks with network.Train() API and automatic gradients
  • 📘 Full TypeScript Support - Complete type definitions for IntelliSense
  • 🎯 Zero Dependencies - Pure WASM + Go runtime, no external libs
  • 🌐 Isomorphic - Works in browsers, Node.js, Bun, and Deno
  • 🎨 Multiple Activation Functions - ReLU, Sigmoid, Tanh, Softplus, LeakyReLU, Linear
  • ⚠️ CPU-Only (GPU support via WebGPU coming soon)

📦 Installation

npm install @openfluke/welvet

Or with your preferred package manager:

# Yarn
yarn add @openfluke/welvet

# pnpm
pnpm add @openfluke/welvet

# Bun
bun add @openfluke/welvet

🚀 Quick Start

import { initLoom, ActivationType } from "@openfluke/welvet";

// Initialize the WASM module
const loom = await initLoom();

// Create a neural network: 784 inputs → 392 hidden → 10 outputs
const network = loom.NewNetwork(784, 1, 1, 2);

// Configure layers using registry-based initialization
const layer0 = loom.CallLayerInit(
  "InitDenseLayer",
  JSON.stringify([784, 392, ActivationType.ReLU])
);
const layer1 = loom.CallLayerInit(
  "InitDenseLayer",
  JSON.stringify([392, 10, ActivationType.Sigmoid])
);

network.SetLayer(JSON.stringify([0, 0, 0, JSON.parse(layer0)]));
network.SetLayer(JSON.stringify([0, 0, 1, JSON.parse(layer1)]));

// Forward pass
const input = new Array(784).fill(0).map(() => Math.random());
const resultJSON = network.ForwardCPU(JSON.stringify([input]));
const [output, duration] = JSON.parse(resultJSON);

console.log("Output:", output);
console.log("Inference time:", duration / 1e6, "ms");

// Training with the high-level API
const batches = [
  { Input: input, Target: [0, 0, 0, 0, 0, 0, 0, 0, 0, 1] }, // Example: classify as "9"
];
const config = {
  Epochs: 10,
  LearningRate: 0.01,
  GradientClip: 1.0,
  LossType: "mse",
};

const trainingResult = network.Train(JSON.stringify([batches, config]));
const result = JSON.parse(trainingResult);
console.log("Final loss:", result.FinalLoss);

📚 API Reference

Initialization

interface InitOptions {
  wasmUrl?: string | URL;      // Custom WASM file location
  injectGoRuntime?: boolean;   // Include Go runtime (default: true)
}

const loom = await initLoom(options?);

Creating Networks

const network = loom.NewNetwork(
  inputSize: number,     // Input layer size
  gridRows: number,      // Grid rows (use 1 for simple networks)
  gridCols: number,      // Grid columns (use 1 for simple networks)
  layersPerCell: number  // Number of layers
);

Layer Types

All layer types are created via the registry system using CallLayerInit():

Dense (Fully-Connected) Layer

const config = loom.CallLayerInit(
  "InitDenseLayer",
  JSON.stringify([
    inputSize: number,
    outputSize: number,
    activation: ActivationType,
  ])
);

Conv2D Layer

const config = loom.CallLayerInit(
  "InitConv2DLayer",
  JSON.stringify([
    height: number,        // Input height
    width: number,         // Input width
    channels: number,      // Input channels
    filters: number,       // Number of output filters
    kernelSize: number,    // Kernel size (e.g., 3 for 3x3)
    stride: number,        // Stride (typically 1 or 2)
    padding: number,       // Padding (typically 0 or 1)
    activation: ActivationType,
  ])
);

Multi-Head Attention Layer

const config = loom.CallLayerInit(
  "InitMultiHeadAttentionLayer",
  JSON.stringify([
    seqLength: number,     // Sequence length
    dModel: number,        // Model dimension
    numHeads: number,      // Number of attention heads
    activation: ActivationType,
  ])
);

RNN Layer

const config = loom.CallLayerInit(
  "InitRNNLayer",
  JSON.stringify([
    inputSize: number,     // Input feature size
    hiddenSize: number,    // Hidden state size
    seqLength: number,     // Sequence length
    outputSize: number,    // Output size (hiddenSize * seqLength)
  ])
);

LSTM Layer

const config = loom.CallLayerInit(
  "InitLSTMLayer",
  JSON.stringify([
    inputSize: number,     // Input feature size
    hiddenSize: number,    // Hidden/cell state size
    seqLength: number,     // Sequence length
    outputSize: number,    // Output size (hiddenSize * seqLength)
  ])
);

Activation Types:

enum ActivationType {
  ReLU = 0, // Scaled ReLU (1.1x)
  Sigmoid = 1, // Logistic sigmoid
  Tanh = 2, // Hyperbolic tangent
  Softplus = 3, // Smooth ReLU
  LeakyReLU = 4, // ReLU with 0.1x negative slope
  Linear = 5, // Identity (no activation)
}

activation: ActivationType // ReLU, Sigmoid, Tanh, Linear );

network.SetLayer(JSON.stringify([ gridRow, // Grid row index gridCol, // Grid column index layerIndex, // Layer index within cell JSON.parse(config) ]));


#### Multi-Head Attention Layer

```typescript
const config = loom.InitMultiHeadAttentionLayer(
  dModel: number,       // Model dimension
  numHeads: number,     // Number of attention heads
  seqLength: number,    // Sequence length
  activation: ActivationType
);

network.SetLayer(JSON.stringify([0, 0, layerIndex, JSON.parse(config)]));

Training Operations

Forward Pass

const input = [0.1, 0.2, 0.3, 0.4];
const resultJSON = network.ForwardCPU(JSON.stringify([input]));
const [output, duration] = JSON.parse(resultJSON);

Backward Pass

const gradOutput = new Array(outputSize).fill(0.01);
const backwardJSON = network.BackwardCPU(JSON.stringify([gradOutput]));
const [gradInput, duration] = JSON.parse(backwardJSON);

Update Weights

const learningRate = 0.01;
network.UpdateWeights(JSON.stringify([learningRate]));

Model Persistence

Save Model

const modelJSON = network.SaveModelToString(JSON.stringify(["model_name"]));
const model = JSON.parse(JSON.parse(modelJSON)[0]);

// Store anywhere (localStorage, IndexedDB, etc.)
localStorage.setItem("my_model", JSON.stringify(model));

Load Model

const savedModel = JSON.parse(localStorage.getItem("my_model")!);
const network = loom.LoadModelFromString(
  JSON.stringify(savedModel),
  "model_name"
);

Runtime Introspection

Get All Methods

const methodsJSON = network.GetMethods();
const methods = JSON.parse(methodsJSON);

methods.forEach((method) => {
  console.log(
    `${method.method_name}(${method.parameters.map((p) => p.type).join(", ")})`
  );
});

Check Method Availability

if (network.HasMethod("ForwardGPU")) {
  const signature = network.GetMethodSignature(JSON.stringify(["ForwardGPU"]));
  console.log(signature);
}

List Method Names

const names = JSON.parse(network.ListMethods());
console.log("Available methods:", names);

🎨 Activation Functions

enum ActivationType {
  ReLU = 0, // Rectified Linear Unit
  Sigmoid = 1, // Sigmoid (logistic)
  Tanh = 2, // Hyperbolic tangent
  Linear = 3, // No activation (identity)
}

💡 Complete Examples

MNIST-Style Classifier

import { initLoom, ActivationType } from "@openfluke/welvet";

async function trainMNIST() {
  const loom = await initLoom();

  // Network: 784 → 128 → 64 → 10
  const network = loom.NewNetwork(784, 1, 1, 3);

  const layer0 = loom.InitDenseLayer(784, 128, ActivationType.ReLU);
  const layer1 = loom.InitDenseLayer(128, 64, ActivationType.ReLU);
  const layer2 = loom.InitDenseLayer(64, 10, ActivationType.Sigmoid);

  network.SetLayer(JSON.stringify([0, 0, 0, JSON.parse(layer0)]));
  network.SetLayer(JSON.stringify([0, 0, 1, JSON.parse(layer1)]));
  network.SetLayer(JSON.stringify([0, 0, 2, JSON.parse(layer2)]));

  // Training loop
  const epochs = 50;
  const learningRate = 0.01;

  for (let epoch = 0; epoch < epochs; epoch++) {
    // Your training data here
    const input = new Array(784).fill(0).map(() => Math.random());
    const target = new Array(10).fill(0);
    target[Math.floor(Math.random() * 10)] = 1;

    // Forward
    const [output] = JSON.parse(network.ForwardCPU(JSON.stringify([input])));

    // Compute loss (MSE)
    const loss =
      output.reduce((sum, val, i) => sum + Math.pow(val - target[i], 2), 0) /
      output.length;

    // Backward
    const gradOutput = output.map(
      (val, i) => (2 * (val - target[i])) / output.length
    );
    network.BackwardCPU(JSON.stringify([gradOutput]));

    // Update
    network.UpdateWeights(JSON.stringify([learningRate]));

    if (epoch % 10 === 0) {
      console.log(`Epoch ${epoch}: Loss = ${loss.toFixed(6)}`);
    }
  }

  // Save model
  const modelJSON = network.SaveModelToString(JSON.stringify(["mnist"]));
  localStorage.setItem("mnist_model", JSON.parse(modelJSON)[0]);
}

XOR Problem

import { initLoom, ActivationType } from "@openfluke/welvet";

const loom = await initLoom();
const network = loom.NewNetwork(2, 1, 1, 2);

// 2 → 4 → 1 (XOR needs hidden layer)
const layer0 = loom.InitDenseLayer(2, 4, ActivationType.ReLU);
const layer1 = loom.InitDenseLayer(4, 1, ActivationType.Sigmoid);

network.SetLayer(JSON.stringify([0, 0, 0, JSON.parse(layer0)]));
network.SetLayer(JSON.stringify([0, 0, 1, JSON.parse(layer1)]));

const trainingData = [
  { input: [0, 0], target: [0] },
  { input: [0, 1], target: [1] },
  { input: [1, 0], target: [1] },
  { input: [1, 1], target: [0] },
];

for (let epoch = 0; epoch < 1000; epoch++) {
  let totalLoss = 0;

  for (const sample of trainingData) {
    const [output] = JSON.parse(
      network.ForwardCPU(JSON.stringify([sample.input]))
    );
    const loss = Math.pow(output[0] - sample.target[0], 2);
    totalLoss += loss;

    const gradOutput = [2 * (output[0] - sample.target[0])];
    network.BackwardCPU(JSON.stringify([gradOutput]));
    network.UpdateWeights(JSON.stringify([0.1]));
  }

  if (epoch % 100 === 0) {
    console.log(`Epoch ${epoch}: Loss = ${(totalLoss / 4).toFixed(6)}`);
  }
}

// Test
trainingData.forEach((sample) => {
  const [output] = JSON.parse(
    network.ForwardCPU(JSON.stringify([sample.input]))
  );
  console.log(
    `${sample.input}${output[0].toFixed(4)} (expected ${sample.target[0]})`
  );
});

🌐 Browser Usage

Via CDN (UMD)

<!DOCTYPE html>
<html>
  <head>
    <script src="https://unpkg.com/@openfluke/welvet"></script>
  </head>
  <body>
    <script>
      (async () => {
        const { initLoom, ActivationType } = window.Welvet;
        const loom = await initLoom();

        const network = loom.NewNetwork(4, 1, 1, 1);
        console.log("LOOM ready!");
      })();
    </script>
  </body>
</html>

Via ES Modules

<!DOCTYPE html>
<html>
  <head>
    <script type="module">
      import {
        initLoom,
        ActivationType,
      } from "https://unpkg.com/@openfluke/welvet/dist/esm/index.js";

      const loom = await initLoom();
      const network = loom.NewNetwork(4, 1, 1, 1);
      console.log("LOOM ready!");
    </script>
  </head>
</html>

⚛️ Framework Integration

React

import { useEffect, useState } from "react";
import { initLoom, type LoomAPI } from "@openfluke/welvet";

function NeuralNetworkComponent() {
  const [loom, setLoom] = useState<LoomAPI | null>(null);
  const [prediction, setPrediction] = useState<number[] | null>(null);

  useEffect(() => {
    initLoom().then((api) => {
      setLoom(api);

      // Initialize network
      const network = api.NewNetwork(4, 1, 1, 2);
      const layer0 = api.InitDenseLayer(4, 8, 0); // ReLU
      const layer1 = api.InitDenseLayer(8, 2, 1); // Sigmoid

      network.SetLayer(JSON.stringify([0, 0, 0, JSON.parse(layer0)]));
      network.SetLayer(JSON.stringify([0, 0, 1, JSON.parse(layer1)]));

      // Make prediction
      const input = [0.5, 0.3, 0.2, 0.1];
      const [output] = JSON.parse(network.ForwardCPU(JSON.stringify([input])));
      setPrediction(output);
    });
  }, []);

  if (!loom) return <div>Loading neural network...</div>;

  return (
    <div>
      <h2>Prediction: {prediction?.map((v) => v.toFixed(4)).join(", ")}</h2>
    </div>
  );
}

Vue 3

<script setup lang="ts">
import { ref, onMounted } from "vue";
import { initLoom, type LoomAPI } from "@openfluke/welvet";

const loom = ref<LoomAPI | null>(null);
const output = ref<number[] | null>(null);

onMounted(async () => {
  const api = await initLoom();
  loom.value = api;

  const network = api.NewNetwork(2, 1, 1, 1);
  const layer = api.InitDenseLayer(2, 1, 1); // Sigmoid
  network.SetLayer(JSON.stringify([0, 0, 0, JSON.parse(layer)]));

  const [result] = JSON.parse(network.ForwardCPU(JSON.stringify([[0.5, 0.5]])));
  output.value = result;
});
</script>

<template>
  <div v-if="!loom">Loading...</div>
  <div v-else>
    <h2>Neural Network Output</h2>
    <pre>{{ output }}</pre>
  </div>
</template>

Svelte

<script lang="ts">
  import { onMount } from 'svelte';
  import { initLoom, type LoomAPI } from '@openfluke/welvet';

  let loom: LoomAPI | null = null;
  let result: number[] = [];

  onMount(async () => {
    loom = await initLoom();

    const network = loom.NewNetwork(3, 1, 1, 1);
    const layer = loom.InitDenseLayer(3, 2, 0); // ReLU
    network.SetLayer(JSON.stringify([0, 0, 0, JSON.parse(layer)]));

    const [output] = JSON.parse(network.ForwardCPU(JSON.stringify([[1, 2, 3]])));
    result = output;
  });
</script>

{#if !loom}
  <p>Loading neural network...</p>
{:else}
  <h2>Result: {result.join(', ')}</h2>
{/if}

🔧 Advanced Configuration

Custom WASM Location

const loom = await initLoom({
  wasmUrl: "/custom/path/loom.wasm",
});

Skip Go Runtime Injection

// Useful if you're loading Go runtime separately
const loom = await initLoom({
  injectGoRuntime: false,
});

📊 Performance Tips

  1. Batch Processing - Process multiple inputs together when possible
  2. Model Caching - Save trained models to avoid retraining
  3. Layer Sizing - Start with smaller layers and scale up as needed
  4. Learning Rate - Tune learning rate for faster convergence (typically 0.001 - 0.1)
  5. Activation Functions - ReLU often trains faster than Sigmoid/Tanh

🐛 Troubleshooting

WASM fails to load

Ensure your server serves .wasm files with the correct MIME type:

Content-Type: application/wasm

Module not found errors

Make sure to await the initialization:

const loom = await initLoom(); // Don't forget await!

JSON parsing errors

All network methods use JSON string parameters:

// ✅ Correct
network.ForwardCPU(JSON.stringify([input]));

// ❌ Wrong
network.ForwardCPU(input);
  • Python Package: welvet - Python bindings for LOOM
  • Go Framework: LOOM - Original Go implementation
  • Legacy Package: @openfluke/portal - Previous generation framework

📄 License

Apache-2.0 © 2025 OpenFluke

🤝 Contributing

Contributions are welcome! Please see the main repository for guidelines.

📞 Support


Built with ❤️ by the OpenFluke team