High-performance Apple Accelerate framework bindings for Node.js. Get up to 296x faster matrix operations and 5-10x faster vector operations on Apple Silicon (M1/M2/M3/M4).
80+ hardware-accelerated functions including BLAS, vDSP, and vForce operations.
Node.js doesn't natively use Apple's Accelerate framework, which provides hardware-optimized routines for numerical computing. This addon exposes Accelerate's BLAS (matrix operations) and vDSP (vector/signal processing) to JavaScript, giving you direct access to:
- AMX (Apple Matrix coprocessor) - Hardware matrix acceleration
- NEON SIMD - Vector processing
- Optimized FFT - Fast Fourier Transform
Note: This package only works on macOS because it uses Apple's Accelerate framework. If you're on Linux ARM64 (Raspberry Pi, AWS Graviton, etc.), consider using OpenBLAS, Eigen, or BLIS instead.
Real benchmarks on Apple M4 Max:
| Operation | Pure JavaScript | node-accelerate | Speedup |
|---|---|---|---|
| Matrix Multiply (500×500) | 86 ms | 0.30 ms | 290x |
| Vector Dot Product (1M) | 0.63 ms | 0.13 ms | 5x |
| Vector Sum (1M) | 0.59 ms | 0.07 ms | 8x |
| Vector Add (1M) | 0.58 ms | 0.19 ms | 3x |
| Trigonometric (10k) | 0.07 ms | 0.008 ms | 8x |
- Matrix multiplication (double & single precision)
- Matrix-vector multiplication
- Matrix transpose
- AXPY, copy, swap operations
- Vector norms and rotations
- Basic operations: add, subtract, multiply, divide, scale, negate
- Dot product, norm, absolute sum
- Element-wise operations: abs, square, sqrt, power, reciprocal
- Multiply-add operations: vma, vmsa
- Normalization to unit length
- Vector utilities: fill, ramp, clear, reverse, copy, swap
- Standard trig: sin, cos, tan (5-10x faster than Math functions)
- Inverse trig: asin, acos, atan, atan2
- Hyperbolic: sinh, cosh, tanh
- Process 1000s of values in microseconds
- exp - Natural exponential
- log, log10 - Natural and base-10 logarithms
- pow - Element-wise power
- Reciprocal and inverse square root
- Basic stats: sum, mean, min, max, minmax
- Variance & standard deviation
- RMS (Root Mean Square)
- Sum of squares, mean magnitude, mean square
- Max/min magnitude
- FFT/IFFT - Fast Fourier Transform (forward & inverse)
- Convolution - 1D convolution for filtering
- Cross-correlation - Signal similarity analysis
- Window functions: Hamming, Hanning, Blackman
- 10-50x faster than pure JavaScript FFT
- Clipping & limiting - Constrain values to range
- Thresholding - Apply threshold to data
- Interpolation - Linear interpolation and lerp
- Rounding: ceil, floor, trunc
- Sign manipulation: copysign
- ✅ Hardware acceleration via AMX & NEON
- ✅ Double precision (Float64Array)
- ✅ Single precision (Float32Array) for matrix ops
- ✅ Zero-copy operations where possible
- ✅ Optimized for Apple Silicon
- ✅ 80+ functions total
npm install @digitaldefiance/node-accelerateRequirements:
- macOS (Apple Silicon: M1/M2/M3/M4 or Intel)
- Node.js >= 18.0.0
- Xcode Command Line Tools
If you don't have Xcode Command Line Tools installed:
xcode-select --installThe package will automatically check your platform during installation. If you see errors:
"node-accelerate requires macOS"
- This package only works on macOS due to Apple's Accelerate framework
- Not supported on Linux or Windows
"Xcode Command Line Tools may not be installed"
- Run:
xcode-select --install - Follow the prompts to install
"Failed to load native module"
- Try rebuilding:
npm rebuild @digitaldefiance/node-accelerate - Ensure Xcode Command Line Tools are installed
node -e "const a = require('@digitaldefiance/node-accelerate'); console.log('✓ Works!')"const accelerate = require('@digitaldefiance/node-accelerate');
// Matrix multiplication: C = A × B
const M = 1000, K = 1000, N = 1000;
const A = new Float64Array(M * K);
const B = new Float64Array(K * N);
const C = new Float64Array(M * N);
// Fill with random data
for (let i = 0; i < A.length; i++) A[i] = Math.random();
for (let i = 0; i < B.length; i++) B[i] = Math.random();
// Hardware-accelerated matrix multiplication
accelerate.matmul(A, B, C, M, K, N);
// Vector operations
const vec1 = new Float64Array(1000000);
const vec2 = new Float64Array(1000000);
const result = new Float64Array(1000000);
for (let i = 0; i < vec1.length; i++) {
vec1[i] = Math.random();
vec2[i] = Math.random();
}
accelerate.vadd(vec1, vec2, result); // result = vec1 + vec2
accelerate.vmul(vec1, vec2, result); // result = vec1 * vec2
const dotProduct = accelerate.dot(vec1, vec2);
const sum = accelerate.sum(vec1);
const mean = accelerate.mean(vec1);
// Statistical operations
const { min, max } = accelerate.minmax(vec1);
const variance = accelerate.variance(vec1);
const stddev = accelerate.stddev(vec1);
// Trigonometric functions (vectorized)
const angles = new Float64Array(1000);
const sines = new Float64Array(1000);
const cosines = new Float64Array(1000);
for (let i = 0; i < 1000; i++) {
angles[i] = (i / 1000) * 2 * Math.PI;
}
accelerate.vsin(angles, sines);
accelerate.vcos(angles, cosines);
// Signal processing
const signal = new Float64Array(65536);
for (let i = 0; i < signal.length; i++) {
signal[i] = Math.sin(2 * Math.PI * i / signal.length);
}
// Apply window and compute FFT
const window = accelerate.hanning(signal.length);
const windowed = new Float64Array(signal.length);
accelerate.vmul(signal, window, windowed);
const spectrum = accelerate.fft(windowed);
console.log(spectrum.real, spectrum.imag);
// Inverse FFT
const reconstructed = accelerate.ifft(spectrum.real, spectrum.imag);
// Convolution for filtering
const kernel = new Float64Array([0.25, 0.5, 0.25]); // Moving average
const filtered = new Float64Array(signal.length - kernel.length + 1);
accelerate.conv(signal, kernel, filtered);
// Data processing
const data = new Float64Array(1000);
for (let i = 0; i < data.length; i++) {
data[i] = Math.random() * 200 - 100;
}
// Clip outliers
const clipped = new Float64Array(1000);
accelerate.vclip(data, clipped, -50, 50);
// Matrix transpose
const matrix = new Float64Array([1, 2, 3, 4, 5, 6]); // 2×3
const transposed = new Float64Array(6); // 3×2
accelerate.transpose(matrix, transposed, 2, 3);Check out the examples/ directory for complete working examples:
machine-learning.js- Neural network operations, softmax, ReLUsignal-processing.js- FFT, filtering, spectral analysisstatistical-operations.js- Mean, variance, std dev, z-scorestrigonometric-functions.js- Vectorized trig operationssignal-processing-advanced.js- Convolution, correlation, windowingmathematical-functions.js- Exp, log, power functionsdata-processing.js- Clipping, thresholding, interpolationmatrix-multiply.js- Matrix operations and benchmarksvector-operations.js- Vector arithmetic examples
Run any example:
node examples/statistical-operations.js
node examples/signal-processing-advanced.jsMatrix multiplication: C = A × B
A: Float64Array - First matrix (M × K) in row-major orderB: Float64Array - Second matrix (K × N) in row-major orderC: Float64Array - Output matrix (M × N) in row-major orderM: number - Rows in A and CK: number - Columns in A, rows in BN: number - Columns in B and C- Returns: Float64Array (C)
Example:
const M = 100, K = 100, N = 100;
const A = new Float64Array(M * K);
const B = new Float64Array(K * N);
const C = new Float64Array(M * N);
// Fill A and B...
accelerate.matmul(A, B, C, M, K, N);Single-precision matrix multiplication (uses Float32Array)
Same parameters as matmul but with Float32Array instead of Float64Array.
Matrix-vector multiplication: y = A × x
A: Float64Array - Matrix (M × N) in row-major orderx: Float64Array - Input vector (N elements)y: Float64Array - Output vector (M elements)M: number - Rows in AN: number - Columns in A- Returns: Float64Array (y)
Matrix transpose: B = A^T
A: Float64Array - Input matrix (rows × cols) in row-major orderB: Float64Array - Output matrix (cols × rows) in row-major orderrows: number - Number of rows in Acols: number - Number of columns in A- Returns: Float64Array (B)
Example:
const A = new Float64Array([1, 2, 3, 4, 5, 6]); // 2×3 matrix
const B = new Float64Array(6); // 3×2 matrix
accelerate.transpose(A, B, 2, 3);AXPY operation: y = alpha*x + y
alpha: number - Scalar multiplierx: Float64Array - Input vectory: Float64Array - Input/output vector- Returns: Float64Array (y)
Copy vector: y = x
x: Float64Array - Input vectory: Float64Array - Output vector- Returns: Float64Array (y)
Swap two vectors: x <-> y
x: Float64Array - First vectory: Float64Array - Second vector- Returns: Float64Array (x)
L2 norm (Euclidean length): ||x||
x: Float64Array - Input vector- Returns: number
Example:
const vec = new Float64Array([3, 4]);
const length = accelerate.norm(vec); // 5Sum of absolute values: sum(|x[i]|)
x: Float64Array - Input vector- Returns: number
Index of maximum absolute value
x: Float64Array - Input vector- Returns: number (index)
Example:
const vec = new Float64Array([1, -5, 3, -2]);
const idx = accelerate.maxAbsIndex(vec); // 1 (value is -5)Givens rotation: apply rotation to vectors x and y
x: Float64Array - First vectory: Float64Array - Second vectorc: number - Cosine of rotation angles: number - Sine of rotation angle- Returns: Float64Array (x)
Dot product: sum(a[i] * b[i])
a: Float64Array - First vectorb: Float64Array - Second vector (same length as a)- Returns: number
Element-wise addition: out[i] = a[i] + b[i]
a: Float64Array - First vectorb: Float64Array - Second vectorout: Float64Array - Output vector- Returns: Float64Array (out)
Element-wise subtraction: out[i] = a[i] - b[i]
a: Float64Array - First vectorb: Float64Array - Second vectorout: Float64Array - Output vector- Returns: Float64Array (out)
Element-wise multiplication: out[i] = a[i] * b[i]
a: Float64Array - First vectorb: Float64Array - Second vectorout: Float64Array - Output vector- Returns: Float64Array (out)
Element-wise division: out[i] = a[i] / b[i]
a: Float64Array - First vectorb: Float64Array - Second vectorout: Float64Array - Output vector- Returns: Float64Array (out)
Vector scaling: b = a * scalar
a: Float64Array - Input vectorscalar: number - Scalar multiplierb: Float64Array - Output vector- Returns: Float64Array (b)
Vector negation: b = -a
a: Float64Array - Input vectorb: Float64Array - Output vector- Returns: Float64Array (b)
Add scalar to vector: c[i] = a[i] + scalar
a: Float64Array - Input vectorscalar: number - Scalar value to addc: Float64Array - Output vector- Returns: Float64Array (c)
Multiply-add: d[i] = (a[i] * b[i]) + c[i]
a: Float64Array - First vectorb: Float64Array - Second vectorc: Float64Array - Third vectord: Float64Array - Output vector- Returns: Float64Array (d)
Example:
const a = new Float64Array([2, 3, 4]);
const b = new Float64Array([5, 6, 7]);
const c = new Float64Array([1, 1, 1]);
const d = new Float64Array(3);
accelerate.vma(a, b, c, d); // d = [11, 19, 29]Multiply-scalar-add: d[i] = (a[i] * b) + c[i]
a: Float64Array - Input vectorb: number - Scalar multiplierc: Float64Array - Vector to addd: Float64Array - Output vector- Returns: Float64Array (d)
Element-wise absolute value: b[i] = |a[i]|
a: Float64Array - Input vectorb: Float64Array - Output vector- Returns: Float64Array (b)
Element-wise square: b[i] = a[i]^2
a: Float64Array - Input vectorb: Float64Array - Output vector- Returns: Float64Array (b)
Element-wise square root: b[i] = sqrt(a[i])
a: Float64Array - Input vectorb: Float64Array - Output vector- Returns: Float64Array (b)
Normalize vector to unit length: b = a / ||a||
a: Float64Array - Input vectorb: Float64Array - Output vector (unit vector)- Returns: Float64Array (b)
Reverse vector order: b = reverse(a)
a: Float64Array - Input vectorb: Float64Array - Output vector- Returns: Float64Array (b)
Fill vector with scalar value
scalar: number - Value to fill withvec: Float64Array - Output vector- Returns: Float64Array (vec)
Example:
const vec = new Float64Array(100);
accelerate.vfill(3.14, vec); // All elements = 3.14Generate linear ramp: vec[i] = start + i * step
start: number - Starting valuestep: number - Step sizevec: Float64Array - Output vector- Returns: Float64Array (vec)
Example:
const vec = new Float64Array(5);
accelerate.vramp(0, 2, vec); // vec = [0, 2, 4, 6, 8]Linear interpolation: c[i] = a[i] + t * (b[i] - a[i])
a: Float64Array - Start vectorb: Float64Array - End vectort: number - Interpolation parameter (0 to 1)c: Float64Array - Output vector- Returns: Float64Array (c)
Example:
const start = new Float64Array([0, 0, 0]);
const end = new Float64Array([10, 20, 30]);
const result = new Float64Array(3);
accelerate.vlerp(start, end, 0.5, result); // result = [5, 10, 15]Clear vector (set all elements to zero)
vec: Float64Array - Vector to clear- Returns: Float64Array (vec)
Limit/saturate values to range [low, high]
a: Float64Array - Input vectorlow: number - Lower boundhigh: number - Upper boundc: Float64Array - Output vector- Returns: Float64Array (c)
Element-wise sine: b[i] = sin(a[i])
a: Float64Array - Input vector (radians)b: Float64Array - Output vector- Returns: Float64Array (b)
Element-wise cosine: b[i] = cos(a[i])
a: Float64Array - Input vector (radians)b: Float64Array - Output vector- Returns: Float64Array (b)
Element-wise tangent: b[i] = tan(a[i])
a: Float64Array - Input vector (radians)b: Float64Array - Output vector- Returns: Float64Array (b)
Example:
const angles = new Float64Array(1000);
const sines = new Float64Array(1000);
for (let i = 0; i < 1000; i++) {
angles[i] = (i / 1000) * 2 * Math.PI;
}
accelerate.vsin(angles, sines);Element-wise inverse sine: b[i] = asin(a[i])
a: Float64Array - Input vector (values in [-1, 1])b: Float64Array - Output vector (radians)- Returns: Float64Array (b)
Element-wise inverse cosine: b[i] = acos(a[i])
a: Float64Array - Input vector (values in [-1, 1])b: Float64Array - Output vector (radians)- Returns: Float64Array (b)
Element-wise inverse tangent: b[i] = atan(a[i])
a: Float64Array - Input vectorb: Float64Array - Output vector (radians)- Returns: Float64Array (b)
Two-argument arctangent: out[i] = atan2(y[i], x[i])
y: Float64Array - Y coordinatesx: Float64Array - X coordinatesout: Float64Array - Output vector (radians)- Returns: Float64Array (out)
Example:
const y = new Float64Array([1, 1, -1, -1]);
const x = new Float64Array([1, -1, -1, 1]);
const angles = new Float64Array(4);
accelerate.vatan2(y, x, angles); // Angles in all four quadrantsElement-wise hyperbolic sine: b[i] = sinh(a[i])
a: Float64Array - Input vectorb: Float64Array - Output vector- Returns: Float64Array (b)
Element-wise hyperbolic cosine: b[i] = cosh(a[i])
a: Float64Array - Input vectorb: Float64Array - Output vector- Returns: Float64Array (b)
Element-wise hyperbolic tangent: b[i] = tanh(a[i])
a: Float64Array - Input vectorb: Float64Array - Output vector- Returns: Float64Array (b)
Example:
// tanh is commonly used as an activation function in neural networks
const logits = new Float64Array(1000);
const activations = new Float64Array(1000);
// ... fill logits ...
accelerate.vtanh(logits, activations);Element-wise exponential: b[i] = exp(a[i])
a: Float64Array - Input vectorb: Float64Array - Output vector- Returns: Float64Array (b)
Element-wise natural logarithm: b[i] = log(a[i])
a: Float64Array - Input vectorb: Float64Array - Output vector- Returns: Float64Array (b)
Element-wise base-10 logarithm: b[i] = log10(a[i])
a: Float64Array - Input vectorb: Float64Array - Output vector- Returns: Float64Array (b)
Element-wise power: c[i] = a[i]^b[i]
a: Float64Array - Base vectorb: Float64Array - Exponent vectorc: Float64Array - Output vector- Returns: Float64Array (c)
Element-wise reciprocal: b[i] = 1 / a[i]
a: Float64Array - Input vectorb: Float64Array - Output vector- Returns: Float64Array (b)
Example:
const values = new Float64Array([2, 4, 5, 10]);
const reciprocals = new Float64Array(4);
accelerate.vreciprocal(values, reciprocals); // [0.5, 0.25, 0.2, 0.1]Element-wise inverse square root: b[i] = 1 / sqrt(a[i])
a: Float64Array - Input vectorb: Float64Array - Output vector- Returns: Float64Array (b)
Example:
// Fast normalization using inverse square root
const vec = new Float64Array([3, 4]);
const sumSq = accelerate.sumOfSquares(vec); // 25
const invLen = new Float64Array([sumSq]);
const invLenResult = new Float64Array(1);
accelerate.vrsqrt(invLen, invLenResult); // 0.2 (1/5)Element-wise ceiling: b[i] = ceil(a[i])
a: Float64Array - Input vectorb: Float64Array - Output vector- Returns: Float64Array (b)
Element-wise floor: b[i] = floor(a[i])
a: Float64Array - Input vectorb: Float64Array - Output vector- Returns: Float64Array (b)
Element-wise truncate (round toward zero): b[i] = trunc(a[i])
a: Float64Array - Input vectorb: Float64Array - Output vector- Returns: Float64Array (b)
Example:
const values = new Float64Array([1.7, -1.7, 2.3, -2.3]);
const ceiled = new Float64Array(4);
const floored = new Float64Array(4);
const truncated = new Float64Array(4);
accelerate.vceil(values, ceiled); // [2, -1, 3, -2]
accelerate.vfloor(values, floored); // [1, -2, 2, -3]
accelerate.vtrunc(values, truncated); // [1, -1, 2, -2]Copy sign: c[i] = |a[i]| * sign(b[i])
a: Float64Array - Magnitude vectorb: Float64Array - Sign vectorc: Float64Array - Output vector- Returns: Float64Array (c)
Example:
const magnitudes = new Float64Array([1, 2, 3, 4]);
const signs = new Float64Array([-1, 1, -1, 1]);
const result = new Float64Array(4);
accelerate.vcopysign(magnitudes, signs, result); // [-1, 2, -3, 4]Clip values to range [min, max]
a: Float64Array - Input vectorb: Float64Array - Output vectormin: number - Minimum valuemax: number - Maximum value- Returns: Float64Array (b)
Example:
const data = new Float64Array([-10, -5, 0, 5, 10]);
const clipped = new Float64Array(5);
accelerate.vclip(data, clipped, -3, 3);
// clipped = [-3, -3, 0, 3, 3]Threshold values: b[i] = a[i] if a[i] > threshold, else threshold
a: Float64Array - Input vectorb: Float64Array - Output vectorthreshold: number - Threshold value- Returns: Float64Array (b)
Sum of all elements
vec: Float64Array - Input vector- Returns: number
Mean (average) of all elements
vec: Float64Array - Input vector- Returns: number
Variance of all elements
vec: Float64Array - Input vector- Returns: number
Standard deviation of all elements
vec: Float64Array - Input vector- Returns: number
Maximum element
vec: Float64Array - Input vector- Returns: number
Minimum element
vec: Float64Array - Input vector- Returns: number
Both minimum and maximum elements
vec: Float64Array - Input vector- Returns: {min: number, max: number}
Example:
const data = new Float64Array([1, 5, 3, 9, 2]);
const stats = accelerate.minmax(data);
console.log(stats.min, stats.max); // 1, 9Root Mean Square: sqrt(sum(vec[i]^2) / n)
vec: Float64Array - Input vector- Returns: number
Sum of squares: sum(vec[i]^2)
vec: Float64Array - Input vector- Returns: number
Mean magnitude: mean(|vec[i]|)
vec: Float64Array - Input vector- Returns: number
Mean square: mean(vec[i]^2)
vec: Float64Array - Input vector- Returns: number
Maximum magnitude (absolute value)
vec: Float64Array - Input vector- Returns: number
Example:
const vec = new Float64Array([1, -5, 3, -2]);
const maxMag = accelerate.maxMagnitude(vec); // 5Minimum magnitude (absolute value)
vec: Float64Array - Input vector- Returns: number
Example:
const vec = new Float64Array([1, -5, 3, -2]);
const minMag = accelerate.minMagnitude(vec); // 1Euclidean distance: sqrt(sum((a[i] - b[i])^2))
a: Float64Array - First vectorb: Float64Array - Second vector- Returns: number
Example:
const point1 = new Float64Array([0, 0, 0]);
const point2 = new Float64Array([3, 4, 0]);
const distance = accelerate.euclidean(point1, point2); // 5Fast Fourier Transform (real to complex)
signal: Float64Array - Input signal (length must be power of 2)- Returns: {real: Float64Array, imag: Float64Array}
Example:
const signal = new Float64Array(1024);
for (let i = 0; i < signal.length; i++) {
signal[i] = Math.sin(2 * Math.PI * i / signal.length);
}
const spectrum = accelerate.fft(signal);
console.log(spectrum.real.length); // 512
console.log(spectrum.imag.length); // 512Inverse Fast Fourier Transform (complex to real)
real: Float64Array - Real part of frequency domainimag: Float64Array - Imaginary part of frequency domain- Returns: Float64Array - Time domain signal
Example:
const signal = new Float64Array(256);
// ... fill signal ...
const spectrum = accelerate.fft(signal);
const reconstructed = accelerate.ifft(spectrum.real, spectrum.imag);
// reconstructed ≈ signal1D Convolution
signal: Float64Array - Input signalkernel: Float64Array - Convolution kernelresult: Float64Array - Output (length = signal.length - kernel.length + 1)- Returns: Float64Array (result)
Example:
const signal = new Float64Array([1, 2, 3, 4, 5]);
const kernel = new Float64Array([0.25, 0.5, 0.25]); // Moving average
const result = new Float64Array(3);
accelerate.conv(signal, kernel, result);Cross-correlation
a: Float64Array - First signalb: Float64Array - Second signalresult: Float64Array - Output (length = a.length + b.length - 1)- Returns: Float64Array (result)
Generate Hamming window
length: number - Window length- Returns: Float64Array - Window coefficients
Generate Hanning window
length: number - Window length- Returns: Float64Array - Window coefficients
Generate Blackman window
length: number - Window length- Returns: Float64Array - Window coefficients
Example:
const window = accelerate.hanning(256);
const signal = new Float64Array(256);
const windowed = new Float64Array(256);
// Apply window to signal
accelerate.vmul(signal, window, windowed);
const spectrum = accelerate.fft(windowed);Linear interpolation
x: Float64Array - X coordinates of data pointsy: Float64Array - Y coordinates of data pointsxi: Float64Array - X coordinates to interpolate atyi: Float64Array - Output interpolated Y values- Returns: Float64Array (yi)
Example:
const x = new Float64Array([0, 1, 2, 3]);
const y = new Float64Array([0, 1, 4, 9]);
const xi = new Float64Array([0.5, 1.5, 2.5]);
const yi = new Float64Array(3);
accelerate.interp1d(x, y, xi, yi);Full TypeScript definitions included:
import * as accelerate from 'node-accelerate';
const A = new Float64Array(100 * 100);
const B = new Float64Array(100 * 100);
const C = new Float64Array(100 * 100);
accelerate.matmul(A, B, C, 100, 100, 100);// Neural network dense layer with activation
function denseLayerWithReLU(input, weights, bias, output) {
const M = 1, K = input.length, N = output.length;
// Matrix multiplication: output = input × weights
accelerate.matmul(input, weights, output, M, K, N);
// Add bias
accelerate.vadd(output, bias, output);
// ReLU activation: max(0, x)
const zeros = new Float64Array(N);
accelerate.vclip(output, output, 0, Infinity);
return output;
}
// Softmax activation
function softmax(logits, output) {
// Subtract max for numerical stability
const maxVal = accelerate.max(logits);
const shifted = new Float64Array(logits.length);
const negMax = -maxVal;
for (let i = 0; i < logits.length; i++) {
shifted[i] = logits[i] + negMax;
}
// Compute exp
accelerate.vexp(shifted, output);
// Normalize
const sum = accelerate.sum(output);
accelerate.vscale(output, 1.0 / sum, output);
return output;
}// Apply windowed FFT for spectral analysis
function spectralAnalysis(audioBuffer, windowSize = 2048) {
const window = accelerate.hanning(windowSize);
const windowed = new Float64Array(windowSize);
// Apply window
accelerate.vmul(audioBuffer, window, windowed);
// Compute FFT
const spectrum = accelerate.fft(windowed);
// Compute magnitude spectrum
const magnitudes = new Float64Array(spectrum.real.length);
for (let i = 0; i < magnitudes.length; i++) {
magnitudes[i] = Math.sqrt(
spectrum.real[i] ** 2 + spectrum.imag[i] ** 2
);
}
// Convert to dB
const dB = new Float64Array(magnitudes.length);
accelerate.vlog10(magnitudes, dB);
accelerate.vscale(dB, 20, dB);
return dB;
}
// Low-pass filter using convolution
function lowPassFilter(signal, cutoffFreq, sampleRate) {
// Design simple FIR filter kernel
const kernelSize = 51;
const kernel = new Float64Array(kernelSize);
// Sinc function kernel
const fc = cutoffFreq / sampleRate;
for (let i = 0; i < kernelSize; i++) {
const x = i - kernelSize / 2;
if (x === 0) {
kernel[i] = 2 * fc;
} else {
kernel[i] = Math.sin(2 * Math.PI * fc * x) / (Math.PI * x);
}
}
// Apply Hamming window to kernel
const window = accelerate.hamming(kernelSize);
accelerate.vmul(kernel, window, kernel);
// Normalize
const sum = accelerate.sum(kernel);
accelerate.vscale(kernel, 1.0 / sum, kernel);
// Convolve
const filtered = new Float64Array(signal.length - kernelSize + 1);
accelerate.conv(signal, kernel, filtered);
return filtered;
}// Numerical integration using trapezoidal rule
function trapezoidalIntegration(f, a, b, n) {
const h = (b - a) / n;
const x = new Float64Array(n + 1);
const y = new Float64Array(n + 1);
// Generate points
for (let i = 0; i <= n; i++) {
x[i] = a + i * h;
y[i] = f(x[i]);
}
// Trapezoidal rule: h * (y[0]/2 + y[1] + ... + y[n-1] + y[n]/2)
const sum = accelerate.sum(y);
return h * (sum - (y[0] + y[n]) / 2);
}
// Compute correlation coefficient
function correlationCoefficient(x, y) {
const n = x.length;
// Compute means
const meanX = accelerate.mean(x);
const meanY = accelerate.mean(y);
// Center the data
const xCentered = new Float64Array(n);
const yCentered = new Float64Array(n);
for (let i = 0; i < n; i++) {
xCentered[i] = x[i] - meanX;
yCentered[i] = y[i] - meanY;
}
// Compute correlation
const numerator = accelerate.dot(xCentered, yCentered);
const denomX = Math.sqrt(accelerate.sumOfSquares(xCentered));
const denomY = Math.sqrt(accelerate.sumOfSquares(yCentered));
return numerator / (denomX * denomY);
}
// Polynomial evaluation using Horner's method (vectorized)
function polyval(coefficients, x, result) {
const n = x.length;
const degree = coefficients.length - 1;
// Initialize with highest degree coefficient
for (let i = 0; i < n; i++) {
result[i] = coefficients[degree];
}
// Horner's method: result = result * x + coeff
for (let i = degree - 1; i >= 0; i--) {
accelerate.vmul(result, x, result);
const coeff = new Float64Array(n);
coeff.fill(coefficients[i]);
accelerate.vadd(result, coeff, result);
}
return result;
}// Compute z-scores (standardization)
function zScore(data, output) {
const mean = accelerate.mean(data);
const std = accelerate.stddev(data);
// z = (x - mean) / std
for (let i = 0; i < data.length; i++) {
output[i] = data[i] - mean;
}
accelerate.vscale(output, 1.0 / std, output);
return output;
}
// Moving average filter
function movingAverage(data, windowSize) {
const kernel = new Float64Array(windowSize);
kernel.fill(1.0 / windowSize);
const result = new Float64Array(data.length - windowSize + 1);
accelerate.conv(data, kernel, result);
return result;
}
// Outlier detection using IQR method
function detectOutliers(data) {
const sorted = new Float64Array(data);
// Note: You'd need to implement sorting or use JS sort
const q1Index = Math.floor(data.length * 0.25);
const q3Index = Math.floor(data.length * 0.75);
const q1 = sorted[q1Index];
const q3 = sorted[q3Index];
const iqr = q3 - q1;
const lowerBound = q1 - 1.5 * iqr;
const upperBound = q3 + 1.5 * iqr;
const outliers = [];
for (let i = 0; i < data.length; i++) {
if (data[i] < lowerBound || data[i] > upperBound) {
outliers.push({ index: i, value: data[i] });
}
}
return outliers;
}// Gaussian blur (separable convolution)
function gaussianBlur(image, width, height, sigma) {
// Generate 1D Gaussian kernel
const kernelSize = Math.ceil(sigma * 6) | 1; // Ensure odd
const kernel = new Float64Array(kernelSize);
const center = Math.floor(kernelSize / 2);
for (let i = 0; i < kernelSize; i++) {
const x = i - center;
kernel[i] = Math.exp(-(x * x) / (2 * sigma * sigma));
}
// Normalize
const sum = accelerate.sum(kernel);
accelerate.vscale(kernel, 1.0 / sum, kernel);
// Horizontal pass
const temp = new Float64Array(width * height);
for (let y = 0; y < height; y++) {
const row = image.subarray(y * width, (y + 1) * width);
const outRow = temp.subarray(y * width, (y + 1) * width);
// Convolve row (simplified - needs padding)
accelerate.conv(row, kernel, outRow);
}
// Vertical pass (similar logic)
// ...
return temp;
}
// Edge detection using Sobel operator
function sobelEdgeDetection(image, width, height) {
const sobelX = new Float64Array([-1, 0, 1, -2, 0, 2, -1, 0, 1]);
const sobelY = new Float64Array([-1, -2, -1, 0, 0, 0, 1, 2, 1]);
const gradX = new Float64Array(width * height);
const gradY = new Float64Array(width * height);
const magnitude = new Float64Array(width * height);
// Apply Sobel kernels (simplified)
// ... convolution logic ...
// Compute gradient magnitude
const gradXSq = new Float64Array(width * height);
const gradYSq = new Float64Array(width * height);
accelerate.vsquare(gradX, gradXSq);
accelerate.vsquare(gradY, gradYSq);
accelerate.vadd(gradXSq, gradYSq, magnitude);
accelerate.vsqrt(magnitude, magnitude);
return magnitude;
}// Calculate returns and volatility
function calculateReturns(prices) {
const returns = new Float64Array(prices.length - 1);
for (let i = 1; i < prices.length; i++) {
returns[i - 1] = (prices[i] - prices[i - 1]) / prices[i - 1];
}
const meanReturn = accelerate.mean(returns);
const volatility = accelerate.stddev(returns);
return { returns, meanReturn, volatility };
}
// Exponential moving average
function exponentialMovingAverage(data, alpha) {
const ema = new Float64Array(data.length);
ema[0] = data[0];
for (let i = 1; i < data.length; i++) {
ema[i] = alpha * data[i] + (1 - alpha) * ema[i - 1];
}
return ema;
}
// Bollinger Bands
function bollingerBands(prices, period, numStdDev) {
const ma = movingAverage(prices, period);
const upper = new Float64Array(ma.length);
const lower = new Float64Array(ma.length);
for (let i = 0; i < ma.length; i++) {
const window = prices.subarray(i, i + period);
const std = accelerate.stddev(window);
upper[i] = ma[i] + numStdDev * std;
lower[i] = ma[i] - numStdDev * std;
}
return { middle: ma, upper, lower };
}Run the included benchmarks:
npm run benchmarkRun tests:
npm testCompare with pure JavaScript:
npm run compare- Reuse buffers - Allocate Float64Arrays once and reuse them
- Batch operations - Process large arrays instead of many small ones
- Use appropriate sizes - Accelerate shines with larger data (1000+ elements)
- Profile your code - Not all operations benefit equally
- Use windows for FFT - Apply Hanning/Hamming windows before FFT for better spectral analysis
- Leverage vectorized trig - Use vsin/vcos/vtan instead of loops with Math.sin/cos/tan
- Chain operations - Minimize intermediate allocations
matmul(A, B, C, M, K, N)- Matrix multiplication (double precision)matmulFloat(A, B, C, M, K, N)- Matrix multiplication (single precision)matvec(A, x, y, M, N)- Matrix-vector multiplicationtranspose(A, B, rows, cols)- Matrix transposeaxpy(alpha, x, y)- AXPY operation (y = alpha*x + y)copy(x, y)- Vector copyswap(x, y)- Vector swapnorm(x)- L2 norm (Euclidean length)abssum(x)- Sum of absolute valuesmaxAbsIndex(x)- Index of maximum absolute valuerot(x, y, c, s)- Givens rotation
dot(a, b)- Dot productvadd(a, b, out)- Element-wise additionvsub(a, b, out)- Element-wise subtractionvmul(a, b, out)- Element-wise multiplicationvdiv(a, b, out)- Element-wise divisionvscale(a, scalar, b)- Scalar multiplicationvneg(a, b)- NegationvaddScalar(a, scalar, c)- Add scalar to vectorvma(a, b, c, d)- Multiply-add: d = (a*b) + cvmsa(a, b, c, d)- Multiply-scalar-add: d = (a*b) + c
vabs(a, b)- Absolute valuevsquare(a, b)- Squarevsqrt(a, b)- Square rootnormalize(a, b)- Normalize to unit lengthvreverse(a, b)- Reverse ordervfill(scalar, vec)- Fill with scalarvramp(start, step, vec)- Generate linear rampvlerp(a, b, t, c)- Linear interpolationvclear(vec)- Clear (set to zero)vlimit(a, low, high, c)- Limit/saturate values
vsin(a, b)- Sinevcos(a, b)- Cosinevtan(a, b)- Tangentvasin(a, b)- Inverse sinevacos(a, b)- Inverse cosinevatan(a, b)- Inverse tangentvatan2(y, x, out)- Two-argument arctangent
vsinh(a, b)- Hyperbolic sinevcosh(a, b)- Hyperbolic cosinevtanh(a, b)- Hyperbolic tangent
vexp(a, b)- Natural exponentialvlog(a, b)- Natural logarithmvlog10(a, b)- Base-10 logarithmvpow(a, b, c)- Power (c = a^b)vreciprocal(a, b)- Reciprocal (1/x)vrsqrt(a, b)- Inverse square root (1/sqrt(x))
vceil(a, b)- Ceilingvfloor(a, b)- Floorvtrunc(a, b)- Truncate (round toward zero)vcopysign(a, b, c)- Copy sign
vclip(a, b, min, max)- Clip to rangevthreshold(a, b, threshold)- Apply threshold
sum(vec)- Sum of elementsmean(vec)- Mean (average)variance(vec)- Variancestddev(vec)- Standard deviationmax(vec)- Maximum valuemin(vec)- Minimum valueminmax(vec)- Both min and maxrms(vec)- Root mean squaresumOfSquares(vec)- Sum of squaresmeanMagnitude(vec)- Mean of absolute valuesmeanSquare(vec)- Mean of squaresmaxMagnitude(vec)- Maximum magnitudeminMagnitude(vec)- Minimum magnitude
euclidean(a, b)- Euclidean distance
fft(signal)- Fast Fourier Transformifft(real, imag)- Inverse FFTconv(signal, kernel, result)- Convolutionxcorr(a, b, result)- Cross-correlation
hamming(length)- Hamming windowhanning(length)- Hanning windowblackman(length)- Blackman window
interp1d(x, y, xi, yi)- Linear interpolation
Total: 80+ functions - All hardware-accelerated via Apple's Accelerate framework
- macOS only - Requires Apple's Accelerate framework
- Float64Array only - Currently supports double precision only
- Row-major order - Matrices must be in row-major format
- FFT size - Must be power of 2
Contributions welcome! See CONTRIBUTING.md for guidelines.
MIT © Jessica Mulein
Built on Apple's Accelerate framework. Inspired by the need for high-performance numerical computing in Node.js on Apple Silicon.
Make sure you installed it:
npm install @digitaldefiance/node-accelerateRebuild the addon:
npm rebuild @digitaldefiance/node-accelerateThis package only works on macOS because it uses Apple's Accelerate framework. It cannot run on Linux or Windows.
Install Xcode Command Line Tools:
xcode-select --installnode-accelerate supports:
- ARM64 (Apple Silicon: M1/M2/M3/M4)
- x64 (Intel Macs)
If you're on an older Mac with a different architecture, this package won't work.
- Make sure you're using large arrays (1000+ elements)
- Reuse buffers instead of allocating new ones
- Run
npm run compareto see actual speedups on your machine - Check that you're not running in a VM or emulator
Made with ❤️ for Apple Silicon